Electron Dialog undefined - node.js

After seen tons of stackoverflow answers about why electron Dialog and ipcMain returns undefined.
I understood that both of them must be called in the main process.
My problem is that I don't know how to declare the main process since I can't call the ipcMain. I don't know if I'm not understanding this well or I'm missing some file (I also try creating a renderer.js file to separate both processes... didn't work).
produces an error:
TypeError: Cannot read property 'showOpenDialog' of undefined
my preload.js is
const {ipcMain} = require('electron').remote
console.log(ipcMain)
my main.js is:
const { app, BrowserWindow } = require('electron')
const path = require('path')
require('electron-reload')(__dirname);
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
Electron version 12.0.5

Related

Wait for an answer from Electron synchronously

I'm trying to make a desktop bybit trading app... and I can't figure it out how to make the code wait for the response from the main script..... I need to wait for a response with the needed info for example like wallet balance. Instead the code runs asynchronously and I the var I need it to render is undefined.... I found the article about the vulnerability of nodeIntegration: true from the author of electron security or smth. So I did everything like he said... but now I can't "pause" the code for the data to receive and render.... here's the code
main.js
const { app, BrowserWindow, ipcMain, Menu } = require('electron');
const path = require('path');
const CryptoJS = require('crypto-js');
const axios = require('axios');
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 385,
height: 200,
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(app.getAppPath(), 'preload.js'), // use a preload script
backgroundThrottling: false,
},
});
// Load app
win.loadFile(path.join(__dirname, './index.html'));
win.setAlwaysOnTop(true, 'screen');
// win.removeMenu();
// rest of code..
}
app.on('ready', createWindow);
ipcMain.on('giveBalance', async e => {
const apiKey = '';
const secret = '';
const timestamp = Date.now().toString();
const params = {
api_key: apiKey,
timestamp: timestamp,
};
let orderedParams = '';
Object.keys(params)
.sort()
.forEach(function (key) {
orderedParams += key + '=' + params[key] + '&';
});
orderedParams = orderedParams.substring(0, orderedParams.length - 1);
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secret);
hmac.update(orderedParams);
const sign = hmac.finalize().toString(CryptoJS.enc.Hex);
const res = await axios.get(`https://api.bybit.com/v2/private/wallet/balance?&api_key=${apiKey}&sign=${sign}&timestamp=${timestamp}`);
let responseObj = res.data.result.USDT.equity.toFixed(2);
console.log(res.data.result.USDT.equity.toFixed(2));
win.webContents.send('balance', { responseObj });
});
preload.js
const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => {
// whitelist channels
let validChannels = ['giveBalance', 'givePosition'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ['balance', 'position'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
},
});
script.js
const getBalance = () => {
window.api.send('giveBalance');
const res = window.api.receive('balance', data => {
console.log('hi');
return data.responseObj;
});
const myBalance = '$' + res;
document.querySelector('.myBalance').textContent = `My Balance: ${myBalance}`;
console.log('not in time');
};
getBalance();
I need to get the balance in the scipt.js, to stop the code untill the res var has received the data... but all of the code gets executed right away and only then it receives a message... thank you..
if I change the part in preload.js to this
changing .on to .sendSync
receive: (channel, func) => {
let validChannels = ['balance', 'position'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.sendSync(channel, (event, ...args) => func(...args));
}
},
then I get
Uncaught Error: An object could not be cloned.
I just needed to put the rest logic inside of the receive message scope...

Electron: Trying to set `nativeTheme.themeSource`, but `nativeTheme` is undefined

I'm unable to set the themeSource of my electron app. Platform is Windows 8.1.
const electron = require('electron');
const app = electron.app;
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
This produces an error in a modal pop-up alert saying nativeTheme is undefined.
I'm definitely doing something wrong, but for the life of me I can't see it.
Everything else I'm doing in Electron works like a charm.
Here's my entire app.js:
// server-side jquery
const jsdom = require('jsdom');
const jquery = require('jquery');
const { JSDOM } = jsdom;
const dom = new JSDOM('<!DOCTYPE html>');
const $ = jquery(dom.window);
global.jq = $;
// for priming the webapp
const request = require('request');
// electron config stuff
var backgroundColor = '#1A1A1A';
var width = 800, height = 600;
// get electron
const electron = require('electron');
// prime electron app
const app = electron.app;
// flags: don't enter GUI launch until both sails & electron report ready
var electronIsReady = false;
var sailsIsReady = false;
var gruntIsReady = false;
// block repeat launches (edge contingency)
var windowIsLaunching = false;
var splashIsUp = false;
var splashResponse = 'pong';
// electron window(s)
var mainWindow = null;
var splashWindow = null;
// delay after all preflight checks pass
var windowCreationDelay = 0;
// sails app address
const appAddress = 'http://127.0.0.1';
const appPort = 1337;
const BrowserWindow = electron.BrowserWindow;
if (app) app.on('ready', tryLaunchingForElectron);
else electronIsReady = true;
function tryLaunchingForSails() {
sailsIsReady = true;
try {
// "prime" the webapp by requesting content early
request(`${appAddress}:${appPort}`, (error,response,body) => {/*nada*/});
if (app && electronIsReady && gruntIsReady) createWindow();
}
catch (e) { console.error(e); }
}
function tryLaunchingForElectron() {
// try to prevent multiple instances of the app running
app.requestSingleInstanceLock();
electronIsReady = true;
if (!splashIsUp) {
splashIsUp = true;
// show splash screen
splashWindow = new BrowserWindow({
width: width, height: height,
transparent: true, frame: false, alwaysOnTop: true,
focusable: false, fullscreenable: false,
webPreferences: { nodeIntegration: true }
});
splashWindow.loadURL(`file://${__dirname}/splash.html`);
}
// enter UI phase if sails is also ready
if (app && sailsIsReady && gruntIsReady) createWindow();
}
function createWindow() {
if (windowIsLaunching === true) return -1;
windowIsLaunching = true;
// optional: give sails time to get it fully together
setTimeout(function() {
try {
// tell the splash page to close
splashResponse = 'close';
// create main window
mainWindow = new BrowserWindow({show: false, width: width, height: height,
backgroundColor: backgroundColor
});
// hide menu bar where available
mainWindow.setMenuBarVisibility(false);
// maximize the window
mainWindow.maximize();
// bring to the front
mainWindow.focus();
// go to the sails app
mainWindow.loadURL(`${appAddress}:${appPort}/`);
// show javascript & DOM consoles
mainWindow.webContents.openDevTools();
// show browser only when it's ready to render itself
mainWindow.once('ready-to-show', function() {
// get the splash out of the way
splashWindow.setAlwaysOnTop(false);
// show the main window
mainWindow.setAlwaysOnTop(true);
mainWindow.show();
mainWindow.setAlwaysOnTop(false);
app.focus();
});
// setup close function
mainWindow.on('closed', function() {
mainWindow = null;
});
}
catch (e) { console.error(e); }
}, windowCreationDelay);
}
// tell the splash window when it's time to hide & close
if (app) app.on('ready', function() {
var ipcMain = electron.ipcMain;
ipcMain.on('splashPing', (event, arg) => {
try {
event.sender.send('splashPing', splashResponse);
} catch (e) { console.log(e); }
if (splashResponse === 'close') {
//splashWindow = null;
ipcMain.removeAllListeners('splashPing');
}
// console.log(`${arg}||${splashResponse}`);
});
});
// quit when all windows are closed
if (app) app.on('window-all-closed', function() {
if (process.platform !== 'darwin') {
sails.lower(function (err) {
if (err) {
console.log(err);
app.exit(1);
} else
app.quit();
setTimeout(()=>{app.quit();},5000);
});
}
});
// probably for mobile
if (app) app.on('activate', function() {
if (mainWindow === null) {
createWindow();
}
})
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
// sails wants this
process.chdir(__dirname);
// import sails & rc
var sails;
var rc;
try {
sails = require('sails');
sails.on('hook:grunt:done', () => {
gruntIsReady = true;
tryLaunchingForSails();
});
rc = require('sails/accessible/rc');
} catch (err) {
console.error(err);
}
// Start server
try {
sails.lift(rc('sails'), tryLaunchingForSails );
}
catch (e) { console.log(e); }
nativeTheme = electron.nativeTheme;
This is the problem. You need to do:
const nativeTheme = electron.nativeTheme;
Although in this case there's no need for the extra variable - just do electron.nativeTheme.themeSource = 'dark';.
I strongly suggest you use Typescript - it would tell you this:
Edit: I'm sure I mentioned this in the comments but it seems to have been removed somehow: You also need to make sure you are using Electron 7.0.0 or greater - nativeTheme was not available before that.

Send data to renderer process

I am using "electron": "^4.1.4" and I am getting my data via knex from an sqlite3 db.
I tried to send the data via an ipcMain call. Find below my main.js
const {
app,
BrowserWindow,
ipcMain
} = require('electron')
// require configuration file
require('dotenv').config()
// database
const knex = require('./config/database')
// services
const {
ScheduledContent
} = require('./service/ScheduledContent')
require('electron-reload')(__dirname);
let mainWindow
const contentService = new ScheduledContent(knex)
ipcMain.on('content-edit', async (e, args) => {
console.log("content-edit - Backend");
let res = await contentService.getAllContent()
event.sender.send('content-edit-res', res)
})
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('./public/index.html')
mainWindow.on('closed', function () {
mainWindow = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
app.on('activate', function () {
if (mainWindow === null) createWindow()
})
My renderer.js looks like the following:
const {
ipcRenderer
} = require('electron')
console.log("loaded renderer.js 123")
document.addEventListener('DOMContentLoaded', pageLoaded);
function pageLoaded() {
console.log('The page is loaded');
ipcRenderer.send('content-edit', 'x')
}
ipcRenderer.on('content-edit-res', (event, arg) => {
console.log(arg)
})
I tried to get the data in the frontend when the page is loaded, then I wanted to append it to my <table id="table1">-tag within my index.html
However, I do not get any data in the frontend.
Any suggestions what I am doing wrong?
I appreciate your replies!
As for now, the problem with code...
// event in receive, not e...
ipcMain.on('content-edit', async (event, args) => {
console.log("content-edit - Backend");
let res = await contentService.getAllContent()
event.sender.send('content-edit-res', res)
})
But, as additional information, you also can send data to renderer process with webContents of window.
const BrowserWindow = electron.BrowserWindow;
let win = new BrowserWindow(<your configs>);
win.webContents.send('message', 'Hello from main!');
You can find useful information here

Electron app has multiple background services running and don't stop on exit

I have just built my first Electron app. But I noticed that 3 services of my app running in background at any time. When the app is activated by the global keyboard shortcut, one instance moves to the active app section.
On exiting, only one instance is gone while the other two keeps running. And I cannot start the app again without stopping these services manually from Task Manager.
app.js
const { app, BrowserWindow, globalShortcut } = require("electron");
const path = require("path");
const url = require("url");
let win = null;
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window.
if (win) {
win.show();
}
});
if (shouldQuit) {
app.quit();
return;
}
app.on("ready", () => {
win = new BrowserWindow({
width: 1000,
height: 600,
transparent: true,
frame: false
});
win.on("close", () => {
win = null;
});
win.loadURL(
url.format({
pathname: path.join(__dirname, "app", "index.html"),
protocol: "file",
slashes: true
})
);
win.once('ready-to-show', () => {
win.show()
});
win.on("blur", () => {
win.hide();
});
win.on('window-all-closed', () => {
globalShortcut.unregisterAll();
if (process.platform !== 'darwin') {
app.quit()
}
});
const ret = globalShortcut.register('CommandOrControl+L', () => {
if(win == null) {
globalShortcut.unregisterAll();
return;
}
win.show();
});
if (!ret) {
console.log('registration failed')
}
app.on('will-quit', () => {
// Unregister all shortcuts.
globalShortcut.unregisterAll();
});
app.on('before-quit', () => {
win.removeAllListeners('close');
globalShortcut.unregisterAll();
win.close();
});
});
The previous error I encountered was that the GlobalShortcut was not deregistered when closing, so I added it to before-quit and window-all-closed events. and that solved it.
Edit:
The above problem only happens on production code. During development, all 3 instance closed together. I am using Windows 10 and Electron 1.6.11.
Just add this to your main javascript file where you included electron module
app.on('window-all-closed', () => {
app.quit()
})

TypeError: keytar.addPassword is not a function on electron

I am trying to use keytar at my electron project, but I got this error:
TypeError: keytar.addPassword is not a function
I saw the docs but it seems that addPassword does not exist.
My main.js is:
const electron = require('electron');
const keytar = require('keytar');
const { app, BrowserWindow } = electron;
const path = require('path');
const url = require('url');
let mainWindow;
let appIcon;
function createWindow() {
keytar.addPassword('KeytarTest', 'AccountName', 'secret');
const secret = keytar.getPassword('KeytarTest', 'AccountName');
console.log(secret);
const { width, height } = electron.screen.getPrimaryDisplay().workAreaSize;
mainWindow = new BrowserWindow({ width, height });
mainWindow.loadURL(startUrl);
const contents = mainWindow.webContents;
mainWindow.on('closed', () => {
mainWindow = null;
});
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
Can anyone help me?
I saw the docs but it seems that addPassword does not exist.
Yes, that's right. The function addPassword does not exist and that's why you're getting this TypeError.
In general, this has nothing to do with Electron, because the keytar package just does not provide the function you were trying to call.
If a function is not mentioned in the docs, it's most likely to not exist.

Resources