Skip to content

Added support for launching the application with a file on MacOS #478

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

Merged
merged 1 commit into from
Jan 10, 2021
Merged
Show file tree
Hide file tree
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
64 changes: 64 additions & 0 deletions ElectronNET.API/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,70 @@ internal set
}
private bool _isReady = false;

/// <summary>
/// Emitted when a MacOS user wants to open a file with the application. The open-file event is usually emitted
/// when the application is already open and the OS wants to reuse the application to open the file.
/// open-file is also emitted when a file is dropped onto the dock and the application is not yet running.
/// <para/>
/// On Windows, you have to parse the arguments using App.CommandLine to get the filepath.
/// </summary>
public event Action<string> OpenFile
{
add
{
if (_openFile == null)
{
BridgeConnector.Socket.On("app-open-file" + GetHashCode(), (file) =>
{
_openFile(file.ToString());
});

BridgeConnector.Socket.Emit("register-app-open-file-event", GetHashCode());
}
_openFile += value;
}
remove
{
_openFile -= value;

if (_openFile == null)
BridgeConnector.Socket.Off("app-open-file" + GetHashCode());
}
}

private event Action<string> _openFile;


/// <summary>
/// Emitted when a MacOS user wants to open a URL with the application. Your application's Info.plist file must
/// define the URL scheme within the CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication.
/// </summary>
public event Action<string> OpenUrl
{
add
{
if (_openUrl == null)
{
BridgeConnector.Socket.On("app-open-url" + GetHashCode(), (url) =>
{
_openUrl(url.ToString());
});

BridgeConnector.Socket.Emit("register-app-open-url-event", GetHashCode());
}
_openUrl += value;
}
remove
{
_openUrl -= value;

if (_openUrl == null)
BridgeConnector.Socket.Off("app-open-url" + GetHashCode());
}
}

private event Action<string> _openUrl;

/// <summary>
/// A <see cref="string"/> property that indicates the current application's name, which is the name in the
/// application's package.json file.
Expand Down
46 changes: 44 additions & 2 deletions ElectronNET.Host/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ let powerMonitor;
let splashScreen, hostHook;
let mainWindowId, nativeTheme;
let dock;
let launchFile;
let launchUrl;

let manifestJsonFileName = 'electron.manifest.json';
let watchable = false;
Expand All @@ -33,6 +35,18 @@ if (watchable) {
manifestJsonFilePath = path.join(currentBinPath, manifestJsonFileName);
}

// handle macOS events for opening the app with a file, etc
app.on('will-finish-launching', () => {
app.on('open-file', (evt, file) => {
evt.preventDefault();
launchFile = file;
})
app.on('open-url', (evt, url) => {
evt.preventDefault();
launchUrl = url;
})
});

const manifestJsonFile = require(manifestJsonFilePath);
if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) {
const mainInstance = app.requestSingleInstanceLock();
Expand Down Expand Up @@ -177,7 +191,7 @@ function startSocketApiBridge(port) {
global['electronsocket'].setMaxListeners(0);
console.log('ASP.NET Core Application connected...', 'global.electronsocket', global['electronsocket'].id, new Date());

appApi = require('./api/app')(socket, app);
appApi = require('./api/app')(socket, app);
browserWindows = require('./api/browserWindows')(socket, app);
commandLine = require('./api/commandLine')(socket, app);
autoUpdater = require('./api/autoUpdater')(socket);
Expand All @@ -194,7 +208,35 @@ function startSocketApiBridge(port) {
browserView = require('./api/browserView')(socket);
powerMonitor = require('./api/powerMonitor')(socket);
nativeTheme = require('./api/nativeTheme')(socket);
dock = require('./api/dock')(socket);
dock = require('./api/dock')(socket);

socket.on('register-app-open-file-event', (id) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels like the wrong place for this addition. There is no other targeted event handling of this nature in main.js. I would think that this belongs in api/app.ts or somewhere similar.

Copy link
Author

Choose a reason for hiding this comment

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

The problem with that is timing. The electron.js docs explicitly state that those events must be done as early as possible in the startup - see https://www.electronjs.org/docs/api/app#event-open-file-macos. I had initially tried putting it into app.ts, but by the time it got there, the ready event had already fired, so it was too late.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about placing it in the app constructor. That looks like it fires before the calls here.

Copy link
Author

Choose a reason for hiding this comment

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

Unfortunately that does not work either. I tried moving it into the app constructor and it did not work. I also added some logging using electron-log to show the order of events:

[2020-10-11 11:39:05.673] [info] old location of will-finish-launching handler
[2020-10-11 11:39:06.805] [info] inside constructor
[2020-10-11 11:39:06.815] [info] old location of register handlers

The first line there is where I have the will-finish-launching handler being installed in the original PR. That is the key one - if you miss that event, the whole thing doesn't work. By the time it gets into the constructor, it is already too late.

electronSocket = socket;

app.on('open-file', (event, file) => {
event.preventDefault();

electronSocket.emit('app-open-file' + id, file);
});

if (launchFile) {
electronSocket.emit('app-open-file' + id, launchFile);
}
});

socket.on('register-app-open-url-event', (id) => {
electronSocket = socket;

app.on('open-url', (event, url) => {
event.preventDefault();

electronSocket.emit('app-open-url' + id, url);
});

if (launchUrl) {
electronSocket.emit('app-open-url' + id, launchUrl);
}
});

try {
const hostHookScriptFilePath = path.join(__dirname, 'ElectronHostHook', 'index.js');
Expand Down
2 changes: 1 addition & 1 deletion buildAll.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ echo "Restore & Build CLI"
pushd $dir/ElectronNET.CLI
dotnet restore
dotnet build
podp
popd

echo "Restore & Build WebApp Demo"
pushd $dir/ElectronNET.WebApp
Expand Down