Electron send data to react with preload.js - javascript

I am trying achieve something where I can directly send data to react and where in react whenever it receives it does something.
So my electron.js is in below format.
// ./public/electron.js
const path = require("path");
const { app, BrowserWindow, ipcMain} = require("electron");
const isDev = false; //require("electron-is-dev"); //false
let splash = null;
let win = null;
let etmf_obj = null;
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
enableRemoteModule: true,
preload: path.join(__dirname, "./preloadDist.js"),
},
});
// win.loadFile("index.html");
win.loadURL(
isDev
? "http://localhost:3000"
: `file://${path.join(__dirname, "../build/index.html")}`
);
// Open the DevTools.
if (!isDev) {
win.webContents.openDevTools({ mode: "undocked" });
}
}
app.whenReady().then(createWindow);
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
function restartApp() {
console.log("restarting app..");
app.exit();
app.relaunch();
}
//IPC SECTION
ipcMain.handle("notify", (event, args) => {
console.log("from react I got" + args);
console.log("hello from electron via react"); //this one works as expected
});
ipcMain.on("splashDone", function () {
console.log("splash done");
});
ipcMain.on("relaunchApp", function () {
restartApp();
});
ipcMain.on("closeAll", function () {
app.quit();
});
ipcMain.on("callAnim", function (args) {
win.webContents.send("showAnimation", args);// trying to send data directly to
//react here but don't know whether its right way or not
});
and my preload file preloadDist.js is in below format
const { ipcRenderer, contextBridge } = require("electron");
contextBridge.exposeInMainWorld("electron", {
notificationApi: {
sendNotification(message) {
ipcRenderer.invoke("notify", message);
},
},
batteryApi: {},
filesApi: {},
splashStatus: {
splashDone() {
ipcRenderer.invoke("splashDone")
}
},
});
and react to call function of or to send data I am do this
for example to send notification data :
<button
className="speak_border"
onMouseEnter={() => setHovermic(true)}
onMouseLeave={() => setHovermic(false)}
onClick={() => {
soundwave();
window.electron.notificationApi.sendNotification(
"From react Hi!");
}}
>
and to receive data I am not able to figure out but as I am doing win.webContents.send("showListenAnimation", args);
I am not able to understand how this will be received at the react end
what I tried is:
useEffect(() => {
try {
window.electron.on(
"showAnimation",
function (event, data) {
if (data) {
setAnim(true);
}
if (!data) {
setAnim(false);
}
}
);
} catch (e) {
console.log("issue with on getting data");
}
});
But this way I am getting error and not able to figure out the way to receive, but sending data from react to electron is working perfectly fine!
Please guide on this and how to achieve with about preload.js and electron.js
format.

Related

Can't get mainWindow.loadURL to load a forged url in Electron

I have the following code (main.js) :
const console = require('console');
const {
app,
BrowserWindow,
BrowserView,
screen,
globalShortcut,
ipcMain
} = require('electron');
const path = require('path');
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
const view = new BrowserView();
mainWindow = new BrowserWindow({
width: 400,
height: 600,
frame: false,
resizable: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
webviewTag: true,
nodeIntegration: true,
contextIsolation: false,
},
});
mainWindow.webContents.openDevTools();
mainWindow.loadFile(path.join(__dirname, 'home.html'));
globalShortcut.register('q', () => {
app.quit()
});
globalShortcut.register('h', () => {
mainWindow.setSize(400, 600);
var xx = Math.round(screen.getPrimaryDisplay().workAreaSize.width / 2 - 200);
var yy = Math.round(screen.getPrimaryDisplay().workAreaSize.height / 2 - 300);
mainWindow.setPosition(xx, yy, true);
mainWindow.resizable = false;
mainWindow.loadFile(path.join(__dirname, 'home.html'));
});
globalShortcut.register('1', () => {
mainWindow.resizable = true;
mainWindow.setPosition(0, mainWindow.getPosition()[1]);
mainWindow.setSize(screen.getPrimaryDisplay().workAreaSize.width, 64);
mainWindow.loadURL('https://example.com?variable=static');
});
globalShortcut.register('2', () => {
var size = mainWindow.getSize();
mainWindow.resizable = true;
mainWindow.setPosition(0, mainWindow.getPosition()[1]);
mainWindow.setSize(size[0], 128);
mainWindow.loadURL('https://example.com?variable=static');
});
globalShortcut.register('3', () => {
var size = mainWindow.getSize();
mainWindow.resizable = true;
mainWindow.setPosition(0, mainWindow.getPosition()[1]);
mainWindow.setSize(size[0], 128 + 55);
mainWindow.loadURL('https://example.com?variable=static');
});
};
app.on('ready', function () {
createWindow();
ipcMain.on('form-submitted', (event, variable) => {
console.log('Form submitted with conference name:', variable);
event.sender.loadURL('https://example.com?variable=' + variable);
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
app.on('will-quit', () => {
globalShortcut.unregisterAll()
})
When I press 1, 2 or 3 the window refreshes the correct URL.
But the moment I set the variable in the URL it stops working; it says it's unable to run jQuery (yes, remote website has javascript).
I wonder if this is a 'security' feature ?
I have tried turning webSecurity parameter to off, no luck.
What am I missing here ?

How to disable error message pop-ups in my electron app?

I wrote an electron app that has to connect to a local server in order to work. Once the program starts, it tries to connect to the server using the Nodejs net library, if one second passes without it connecting to the server the program will try to connect again, once a connection to the server is established the program will continue as normal.
The issue I am having is when my client can't find, or connect, to the server on the network, I get a pop-up error message which I don't want to show.
The image below shows the error message I am receiving, I am using Ubuntu but I get the same message on windows:
And just for reference here is my relevant code:
main.js:
const express = require('express');
const ChatApp = express();
require('./backend/scripts/javascript/net_receiver.js');
require('./backend/scripts/javascript/websocket_receiver.js');
ChatApp.use(express.static('./frontend'));
ChatApp.listen(3003, function()
{
console.log('\nControl Panel Server Running on Port 3003');
});
const { app, BrowserWindow } = require("electron");
app.commandLine.appendSwitch('ignore-certificate-errors');
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true
let mainWindow;
function createWindow()
{
mainWindow = new BrowserWindow(
{
icon: 'frontend/chat_app/css/App_Logo.jpg',
width: 700,
height: 830,
//autoHideMenuBar: true,
//fullscreen: true,
webPreferences:
{
nodeIntegration: true,
}
});
mainWindow.loadURL("http://localhost:3003");
mainWindow.on("closed", function ()
{
mainWindow = null;
});
}
app.on("ready", createWindow);
app.on("resize", function (e, x, y)
{
mainWindow.setSize(x, y);
});
app.on("window-all-closed", function ()
{
if (process.platform !== "darwin")
{
app.quit();
}
});
app.on("activate", function ()
{
if (mainWindow === null)
{
createWindow();
}
});
net_client_sender.js
const net = require('net');
const client = new net.Socket();
const settings = require("../../settings/comms_settings.json");
netClient = {}
netClient.client = client;
netClient.ConnectToPLC = () =>
{
client.connect(settings["modbusPORT"], settings["plcIPv4"]);
}
netClient.SendRequest = (msg) =>
{
client.write(msg);
}
module.exports = netClient;
net_client_receiver.js
api = require("./api.js");
netClient = require("./net_client.js");
var connected = false;
/*Check if there is a connection to the PLC,*/
/*if not wait a moment and try to connect again*/
var CheckConnection = () =>
{
/*if there is no connection to PLC*/
var connectionTester = setInterval(() =>
{
if (connected === false)
{
console.log("Connection to PLC failed!");
console.log("Retrying connection...\n\n");
/*Retry connection to PLC*/
netClient.ConnectToPLC();
}
/*if there is a connection to the PLC*/
else
{
/*stop trying to connect to the PLC*/
clearInterval(connectionTester);
}
}, 1000);
}
netClient.client.on("timeout", () =>
{
connected = false;
CheckConnection();
});
netClient.client.on("connect", () =>
{
connected = true;
api.StartMainLoop();
});
netClient.client.on("data", (data) =>
{
api.ReadModbusMessage(data);
});
netClient.ConnectToPLC();
CheckConnection();
So just to clarify, I am not concerned with the error itself, but rather just with the fact that the error is shown in a pop-up message. So if someone can please tell me what to do to make it not show it would help a lot.

Function in Electron does not work but in Node does

I'm running the same function from both Electron and Node, but obtaining different results:
Invoking the function using Node from command line works fine, I get the list of connected devices:
node my_func.js
var iosDevice = require("node-ios-device");
iosDevice.devices(function (err, devices) {
if (err) {
console.error("Error!", err);
} else {
console.log(devices);
}
});
But putting the same chunk of code in main.js in Electron
(it's not in any other renderer file and it is just invoked from command line using the standard: $ npm run start),
yields the following outcome:
main.js
const { app, BrowserWindow } = require("electron");
var iosDevice = require("node-ios-device");
iosDevice.devices(function (err, devices) {
if (err) {
console.error("Error!", err);
} else {
console.log(devices);
}
});
// SET ENVIRONTMENT
process.env.NODE_ENV = "development";
// process.env.NODE_ENV = "production";
const isDev = process.env.NODE_ENV !== "production" ? true : false;
// CHECK PLATFORM
const isMac = process.platform === "darwin" ? true : false;
let mainWindow;
function createMainWindow() {
mainWindow = new BrowserWindow({
title: "test",
width: 700,
height: 195,
icon: "./assets/icons/Icon_256x256.png",
resizable: isDev ? true : false,
backgroundColor: "white",
show: false,
webPreferences: {
nodeIntegration: true,
// sandbox: true,
// contextIsolation: false,
},
});
mainWindow.loadFile("./app/index.html");
mainWindow.webContents.on("did-finish-load", function () {
mainWindow.show();
});
}
app.on("ready", () => {
createMainWindow();
});
app.on("window-all-closed", () => {
if (!isMac) {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
});
The console does not show any result
Apparently, three instances of the app get created, 3 app icons pops up in Dock:
Three app icons show up
this is the package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "Viking",
"license": "ISC",
"devDependencies": {
"electron": "^11.4.4"
},
"dependencies": {
"node-ios-device": "^1.9.2"
}
}
and requiring the npm "node-ios-device" package does not present any issue.
Any idea about how I can accomplish the same task using Electron instead Node?
Thank you.
UPDATE
After trying difference things this is the most close I got to a solution:
I moved the function to another file and I call it from the main.js file spawning a child process and invoking the function using node:
ipcMain.on("dev:getinfo", (event, options) => {
let child_proc;
let devices;
child_proc = spawn("node", ["device.js"]);
child_proc.stdout.on("data", (data) => {
devices = `${data}`;
});
child_proc.on("exit", function (code, signal) {
console.log(devices);
});
});
And it works, in development...
but I have the feeling that it's not going to work in production since the users I'd like to distribute this app do not have Node.js installed and I don't know how I could bundle electron in such way that the users don't need to have Node.js installed.
So an issue was resolved but another one showed up 😐
Further info about my comment above...
If you want to get access to the iOSdevices from the main process to the renderer (that is the electron frontend or the webpage), try to use the
webContents or the ipcMain/ipcRenderer event emitters.
// In the main process.
const { app, BrowserWindow } = require("electron");
const { iosDevice } = require("node-ios-device");
-- previous snips --
let win = null
app.whenReady().then(() => {
win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL(`file://${__dirname}/index.html`)
win.webContents.on('did-finish-load', () => {
let devices = iosDevice.devices(function (err, devices) {
if (err) {
console.error("Error!", err);
} else {
console.log(devices);
return devices;
}
});
win.webContents.send('send-devices', devices);
})
})
Then in your index.html..
<!-- index.html -->
<html>
<body>
<script>
require('electron').ipcRenderer.on('send-devices', (event, message) => {
console.log(message) // Prints the devices
})
</script>
</body>
</html>
// https://www.electronjs.org/docs/api/web-contents#contentssendchannel-args

How to move ipcMain.on calls outside the main.js file

I am working on a project with Electron and React. I am going to be making multiple calls to the database via ipcMain and ipcRenderer so I moved the calls for ipcMain to another file(ipcMainHandler.js).
The challenge I am facing now is how to send responses back to the ipcRenderer. I am unable to access the mainWindow from within that file.
This is the code for my main file.
const url = require('url');
const { app, BrowserWindow } = require('electron');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
preload: __dirname + '/preload.js'
},
});
const startUrl =
process.env.ELECTRON_START_URL ||
url.format({
pathname: path.join(__dirname, './build/index.html'),
protocol: 'file:',
slashes: true,
});
mainWindow.loadURL(startUrl);
mainWindow.webContents.openDevTools();
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();
}
});
require('./src/server/helpers/ipcMainHandler.js');
The ipcMainHandler.js file
const { SIGNIN_REQUEST, SIGNIN_RESPONSE } = require('../../common/events.js');
const { ipcMain } = require('electron');
ipcMain.on(SIGNIN_REQUEST, (event, data) => {
const user = AuthController.userSignin({ ...data });
});
Things I have tried
Accessing the currentWindow from remote - throws a remote undefined error
Adding the mainWindow to a global variable and trying to access it in the ipcHander. - This also returns an undefined message.
This has been resolved. I used the event object to send the response from ipcMain.
ipcMain.on(SIGNIN_REQUEST, async (event, data) => {
const user = await AuthController.userSignin({ ...data });
event.sender.send(SIGNIN_RESPONSE, user);
});

Electron JS - Get path of chosen directory

Im fairly new in the programming world. I'm making an app where it should be possible to choose a directory, where to save some generated files.
I'm working with the ipc, and it seems like some of the code works, but it looks like i can't get the mainIpc to send the path back to the renderer.
I hope the hive can help, thanks in advance!
Renderer:
const electron = require("electron");
const ipc = require("electron").ipcRenderer;
createBtn.addEventListener("click", (event) => {
ipc.send("path:get");
});
ipc.on("path:selected", function (path) {
console.log("Full path: ", path);
});
Main
const ipc = require("electron").ipcMain;
const os = require("os");
const { dialog } = require("electron");
ipc.on("path:get", function (event) {
if (os.platform() === "linux" || os.platform() === "win32") {
dialog.showOpenDialog(
{
properties: ["openFile"],
},
function (files) {
if (files) win.webContents.send("path:selected", files[0]);
console.log("SENT");
}
);
} else {
dialog.showOpenDialog(
{
properties: ["openFile", "openDirectory"],
},
function (files) {
if (files) win.webContents.send("path:selected", files[0]);
console.log("SENT");
}
);
}
});
Edit: Adding the setup
Setup
const { app, BrowserWindow } = require("electron");
const ipc = require("electron").ipcMain;
const os = require("os");
const { dialog } = require("electron");
try {
require("electron-reloader")(module);
} catch (_) {}
let win;
function createWindow() {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
},
});
win.loadFile("./src/index.html");
}
app.whenReady().then(createWindow);
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
I figured it out with some kind help.
So if anyone needs the same procedure i'll try to explain what i got to.
So, in the main, i had to add a then, because the showDialog returns a promise
if (os.platform() === "linux" || os.platform() === "win32") {
dialog
.showOpenDialog({
properties: ["openFile", "openDirectory"],
})
.then((result) => {
if (result) win.webContents.send("path:selected", result.filePaths);
})
.catch((err) => {
console.log(err);
});
} else {
dialog
.showOpenDialog({
properties: ["openFile", "openDirectory"],
})
.then((result) => {
console.log(result.filePaths);
if (result) win.webContents.send("path:selected", result.filePaths);
})
.catch((err) => {
console.log(err);
});
}
});
This sends back an array with the path at [0]
in the renderer i forgot to add the event as an parameter.
ipc.on("path:selected", (event, path) => {
chosenPath = path;
console.log("Full path: ", chosenPath[0]);
});

Categories

Resources