Can't get initial app configuration from main
donadiomauro opened this issue · comments
Hi,
I anticipate I'm not an Electron expert.
I am using secure-electron-template
as a valid and strong starting point for my application.
The issue I am having is with the store.
I understand the data-flow for secure-electron-store
but I'm struggling to make main
reading it.
I need to read the app configuration (options such as "Start at login") and set them in the tray menu. This means it has to happen at the app bootstrap and can't wait for the window (or React app) to render.
I have tried in many ways, even triggering a useConfigInMainRequest
from the main
file but with no avail and I am now a bit lost.
How can I achieve it by keeping the best practices?
PS: I know this is not the best place where to ask for implementation help but for issues. However, I haven't found any other forum where to ask.
Thanks,
Mauro
Hi @donadiomauro,
What exactly are you trying to configure? Can you share your main code? You are using a tray?
The best fix is to minimize the app, and after configuring what you need, maximize the app as shown here. You are saying this process doesn't work for your scenario?
Hi @reZach ,
Thank you for your response.
I started with secure-electron-template
and there is still no custom code added, only the Tray block.
In fact, I am using Tray and added the following:
app.whenReady().then(() => {
tray = new Tray(path.join(__dirname, "../assets/IconTemplate.png"));
const contextMenu = Menu.buildFromTemplate([
{ label: "Start at login", checked: configVariableFromStoreAtBootstrap },
{ label: "Preferences" },
{ type: "separator" },
{ role: "quit" },
]);
tray.setToolTip("This is my application.");
tray.setContextMenu(contextMenu);
});
In theory, I would get the value of configVariableFromStoreAtBootstrap
form the store and set it during the main
bootstrap.
Using the minimize-and-maximise instructions don't allow me to set configVariableFromStoreAtBootstrap
because main
has already been initialised. So, in my case, the Tray icon wouldn't be properly initialised.
Thanks,
Mauro
whenReady
is fired after the Electron app is intialized (https://www.electronjs.org/docs/api/app#event-ready), so I don't think running the code after the app has started presents any problems.
I think you need a few modifications:
- Hold a reference to
tray
outside thewhenReady
block, it may be getting garbage collected and deleted before you can see it. Similar to howwin
is set up in the template:
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
- Instead of initializing the tray in
whenReady
, just set a variable that holds the event was called. I'm suggesting, then, to initialize the tray in the callback, perhaps polling with an interval for this variable to be1
ortrue
, in here:
// Sets up main.js bindings for our electron store;
// callback is optional and allows you to use store in main process
const callback = function(success, store){
console.log(`${!success ? "Un-s" : "S"}uccessfully retrieved store in main process.`);
console.log(store); // {"key1": "value1", ... }
win.maximize(); // modify BrowserWindow, for example
};
Give this a try, hopefully it will work.
Hi @reZach ,
I have tried the solution you suggested and it sorted out part of the issue.
I can now set up the initial state for the Tray in the callback function.
However, in order to update the state, I will need an onClick event. The action of this event will update the store state from the tray menu. This causes an error.
In code-terms, I have changed main
as follows:
// main.js
// added tray as a global variable
let tray;
// main.js
const callback = function (success, store) {
console.log(
`${!success ? "Un-s" : "S"}uccessfully retrieved store in main process.`
);
console.log(store); // {"key1": "value1", ... }
// if tray is not initialised
if (!tray) {
console.log(
`store.startAtLogin contains ${store.startAtLogin} and I am now creating the Tray`
);
tray = new Tray(path.join(__dirname, "../assets/IconTemplate.png"));
const contextMenu = Menu.buildFromTemplate([
{
label: "Start at login",
type: "checkbox",
checked: store.startAtLogin,
click: (event) => {
// *** this won't work here ***
store.send(writeConfigRequest, "startAtLogin", event.checked);
},
},
{ type: "separator" },
{ label: "Preferences" },
{ role: "quit" },
]);
tray.setToolTip("This is my application.");
tray.setContextMenu(contextMenu);
}
win.maximize();
};
The callback of the onClick event in the tray will generate an error. There, I don't know how to update the store.
// root.jsx
import {
writeConfigRequest,
useConfigInMainRequest,
} from "secure-electron-store";
// ...
componentDidMount() {
// Request so that the main process can use the store
window.api.store.send(useConfigInMainRequest);
window.api.store.send(writeConfigRequest, "startAtLogin", true);
}
// ...
@donadiomauro what's the error? I'm guessing store isn't defined?
yes, that's correct, I get the following error:
A JavaScript error occurred in the main process
Uncaught Exception:
TypeError: store.send is not a function
at click (/XYZ/app/electron/main.js:97:19)
at MenuItem.click (electron/js2c/browser_init.js:1549:9)
at Function.executeCommand (electron/js2c/browser_init.js:1810:13)
I have also tried with the following, but it was more a blind test-and-fail than something well pondered.
ipcMain.store.send(writeConfigRequest, "startAtLogin", event.checked);
// and
win.store.send(writeConfigRequest, "startAtLogin", event.checked);
@donadiomauro as I expected.
This can help point you in the right direction, if it doesn't work outright. The click event has three parameters, and the error is occurring because store is not defined in this function.
Something like this is ugly, but I think it can work
const callback = function (success, store) {
// if tray is not initialized
if (!tray) {
console.log(
`store.startAtLogin contains ${store.startAtLogin} and I am now creating the Tray`
);
tray = new Tray(path.join(__dirname, "../assets/IconTemplate.png"));
const clickHandler = function(menuItem, browserWindow, event){
this.send(writeConfigRequest, "startAtLogin", event.checked);
}.bind(store);
const contextMenu = Menu.buildFromTemplate([
{
label: "Start at login",
type: "checkbox",
checked: store.startAtLogin,
click: clickHandler
},
{ type: "separator" },
{ label: "Preferences" },
{ role: "quit" },
]);
tray.setToolTip("This is my application.");
tray.setContextMenu(contextMenu);
}
win.maximize();
};
Otherwise,.. one could look at this code for inspiration, that would likely be necessary to be added to this package for it to work the way you intend it.
@donadiomauro Didn't mean to close this, Github is getting smarter!
I pushed changes to the store which should allow you to do what you originally asked with this issue. Can you please review, [and re-open if necessary]?