Skip to content

Commit

Permalink
Save dialog for closing temporary sketch and unsaved files (#893)
Browse files Browse the repository at this point in the history
* Use normal `OnWillStop` event

* Align `CLOSE` command to rest of app

* Fixed FS path vs encoded URL comparision when handling stop request.

Ref: eclipse-theia/theia#11226
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* Fixed the translations.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* Fixed the translations again.

Removed `electron` from the `nls-extract`. It does not contain app code.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

* Aligned the stop handler code to Theia.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
  • Loading branch information
2 people authored and Alberto Iannaccone committed Jun 3, 2022
1 parent 3f472f0 commit bee7830
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import {
DisposableCollection,
} from '@theia/core';
import {
Dialog,
FrontendApplication,
FrontendApplicationContribution,
LocalStorageService,
OnWillStopAction,
SaveableWidget,
StatusBar,
StatusBarAlignment,
Expand Down Expand Up @@ -667,4 +669,51 @@ export class ArduinoFrontendContribution
}
);
}

onWillStop(): OnWillStopAction {
return {
reason: 'temp-sketch',
action: () => {
return this.showTempSketchDialog();
}
}
}

private async showTempSketchDialog(): Promise<boolean> {
const sketch = await this.sketchServiceClient.currentSketch();
if (!sketch) {
return true;
}
const isTemp = await this.sketchService.isTemp(sketch);
if (!isTemp) {
return true;
}
const messageBoxResult = await remote.dialog.showMessageBox(
remote.getCurrentWindow(),
{
message: nls.localize('arduino/sketch/saveTempSketch', 'Save your sketch to open it again later.'),
title: nls.localize('theia/core/quitTitle', 'Are you sure you want to quit?'),
type: 'question',
buttons: [
Dialog.CANCEL,
nls.localizeByDefault('Save As...'),
nls.localizeByDefault("Don't Save"),
],
}
)
const result = messageBoxResult.response;
if (result === 2) {
return true;
} else if (result === 1) {
return !!(await this.commandRegistry.executeCommand(
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
{
execOnlyIfTemp: false,
openAfterMove: false,
wipeOriginal: true
}
));
}
return false
}
}
73 changes: 1 addition & 72 deletions arduino-ide-extension/src/browser/contributions/close.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ArduinoMenus } from '../menu/arduino-menus';
import { SaveAsSketch } from './save-as-sketch';
import {
SketchContribution,
Command,
Expand All @@ -33,76 +31,7 @@ export class Close extends SketchContribution {

registerCommands(registry: CommandRegistry): void {
registry.registerCommand(Close.Commands.CLOSE, {
execute: async () => {
// Close current editor if closeable.
const { currentEditor } = this.editorManager;
if (currentEditor && currentEditor.title.closable) {
currentEditor.close();
return;
}

// Close current widget from the main area if possible.
const { currentWidget } = this.shell;
if (currentWidget) {
const currentWidgetInMain = toArray(
this.shell.mainPanel.widgets()
).find((widget) => widget === currentWidget);
if (currentWidgetInMain && currentWidgetInMain.title.closable) {
return currentWidgetInMain.close();
}
}

// Close the sketch (window).
const sketch = await this.sketchServiceClient.currentSketch();
if (!sketch) {
return;
}
const isTemp = await this.sketchService.isTemp(sketch);
const uri = await this.sketchServiceClient.currentSketchFile();
if (!uri) {
return;
}
if (isTemp && (await this.wasTouched(uri))) {
const { response } = await remote.dialog.showMessageBox({
type: 'question',
buttons: [
nls.localize(
'vscode/abstractTaskService/saveBeforeRun.dontSave',
"Don't Save"
),
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
nls.localize(
'vscode/abstractTaskService/saveBeforeRun.save',
'Save'
),
],
message: nls.localize(
'arduino/common/saveChangesToSketch',
'Do you want to save changes to this sketch before closing?'
),
detail: nls.localize(
'arduino/common/loseChanges',
"If you don't save, your changes will be lost."
),
});
if (response === 1) {
// Cancel
return;
}
if (response === 2) {
// Save
const saved = await this.commandService.executeCommand(
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
{ openAfterMove: false, execOnlyIfTemp: true }
);
if (!saved) {
// If it was not saved, do bail the close.
return;
}
}
}
window.close();
},
execute: () => remote.getCurrentWindow().close()
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ import { MainMenuManager } from '../../../common/main-menu-manager';
import { ElectronWindowService } from '../../electron-window-service';
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
import { ElectronMenuContribution } from './electron-menu-contribution';
import { nls } from '@theia/core/lib/common/nls';

import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dialogs from '@theia/core/lib/browser/dialogs';


Object.assign(dialogs, {
confirmExit: async () => {
const messageBoxResult = await remote.dialog.showMessageBox(
remote.getCurrentWindow(),
{
message: nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'),
title: nls.localize('theia/core/quitTitle', 'Are you sure you want to quit?'),
type: 'question',
buttons: [
dialogs.Dialog.CANCEL,
dialogs.Dialog.YES,
],
}
)
return messageBoxResult.response === 1;
}
});

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMenuContribution).toSelf().inSingletonScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
IDEUpdaterPath,
} from '../common/protocol/ide-updater';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
import { TheiaElectronWindow } from './theia/theia-electron-window';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainApplication).toSelf().inSingletonScope();
Expand Down Expand Up @@ -56,4 +58,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
)
)
.inSingletonScope();

bind(TheiaElectronWindow).toSelf();
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { injectable } from '@theia/core/shared/inversify';
import { StopReason } from '@theia/core/lib/electron-common/messaging/electron-messages';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { FileUri } from '@theia/core/lib/node';
import URI from '@theia/core/lib/common/uri';

@injectable()
export class TheiaElectronWindow extends DefaultTheiaElectronWindow {
protected async handleStopRequest(
onSafeCallback: () => unknown,
reason: StopReason
): Promise<boolean> {
// Only confirm close to windows that have loaded our frontend.
// Both the windows's URL and the FS path of the `index.html` should be converted to the "same" format to be able to compare them. (#11226)
// Notes:
// - Windows: file:///C:/path/to/somewhere vs file:///c%3A/path/to/somewhere
// - macOS: file:///Applications/App%20Name.app/Contents vs /Applications/App Name.app/Contents
// This URL string comes from electron, we can expect that this is properly encoded URL. For example, a space is `%20`
const currentUrl = new URI(this.window.webContents.getURL()).toString();
// THEIA_FRONTEND_HTML_PATH is an FS path, we have to covert to an encoded URI string.
const frontendUri = FileUri.create(
this.globals.THEIA_FRONTEND_HTML_PATH
).toString();
const safeToClose =
!currentUrl.includes(frontendUri) || (await this.checkSafeToStop(reason));
if (safeToClose) {
try {
await onSafeCallback();
return true;
} catch (e) {
console.warn(`Request ${StopReason[reason]} failed.`, e);
}
}
return false;
}
}
7 changes: 4 additions & 3 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@
},
"common": {
"later": "Later",
"loseChanges": "If you don't save, your changes will be lost.",
"noBoardSelected": "No board selected",
"notConnected": "[not connected]",
"offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.",
"oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?",
"processing": "Processing",
"saveChangesToSketch": "Do you want to save changes to this sketch before closing?",
"selectBoard": "Select Board",
"selectedOn": "on {0}",
"serialMonitor": "Serial Monitor",
Expand Down Expand Up @@ -292,6 +290,7 @@
"openSketchInNewWindow": "Open Sketch in New Window",
"saveFolderAs": "Save sketch folder as...",
"saveSketchAs": "Save sketch folder as...",
"saveTempSketch": "Save your sketch to open it again later.",
"showFolder": "Show Sketch Folder",
"sketch": "Sketch",
"sketchbook": "Sketchbook",
Expand Down Expand Up @@ -320,7 +319,9 @@
"cannotConnectDaemon": "Cannot connect to the CLI daemon.",
"couldNotSave": "Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.",
"daemonOffline": "CLI Daemon Offline",
"offline": "Offline"
"offline": "Offline",
"quitMessage": "Any unsaved changes will not be saved.",
"quitTitle": "Are you sure you want to quit?"
},
"debug": {
"start": "Start...",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"test": "lerna run test",
"download:plugins": "theia download:plugins",
"update:version": "node ./scripts/update-version.js",
"i18n:generate": "theia nls-extract -e vscode -f \"+(arduino-ide-extension|browser-app|electron|electron-app|plugins)/**/*.ts?(x)\" -o ./i18n/en.json",
"i18n:generate": "theia nls-extract -e vscode -f \"+(arduino-ide-extension|browser-app|electron-app|plugins)/**/*.ts?(x)\" -o ./i18n/en.json",
"i18n:check": "yarn i18n:generate && git add -N ./i18n && git diff --exit-code ./i18n",
"i18n:push": "node ./scripts/i18n/transifex-push.js ./i18n/en.json",
"i18n:pull": "node ./scripts/i18n/transifex-pull.js ./i18n/",
Expand Down

0 comments on commit bee7830

Please sign in to comment.