Electron .ipcRenderer fires twice - electron

I'm creating (for the first time) a small Mac only app using Electron.
I am trying to use ipcRenderer to communicate between my app menu and the content in the main BrowserWindow.
I have the menu set up as follows to send the message 'select-active':
const {Menu} = require('electron')
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const template = [
{
label: 'Fonts',
submenu: [
{
label: 'Select All Acitve Fonts',
accelerator: 'Command+A',
click (item, focusedWindow) { if(focusedWindow) focusedWindow.webContents.send('select-active') }
},...
which I am then receiving as follows:
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('select-active', function () {
console.log('SELECTED');
})
The problem I have is that every time the menu command is selected the message is logged twice in the console. Where am I going wrong?

How about using .once instead
ipcRenderer.once('select-active', function () {
console.log('SELECTED');
})

You are subscribing to ipcRenderer.on after React component rerendering (React calls your function every time it needs to get rendered version of it). Try to define the ipcRenderer.on event handler outside of React component function.
function yourFunction(){
return(){}
}
export default yourFunction;
ipcRenderer.send();
Place your ipcRenderer.send(); under the export default yourFunction;
It`s works for me!

Related

Electron Deeplink is not working, it does not respond at all with "gotTheLock", unless I run it directly via CMD line calling electron.exe

I've been trying to get deeplinks to work to do oauth for like 4 weeks:
on different boilerplates
on raw quick start of electron
on electron-forge with webpack.
None of them work while in dev mode, and only does while packaged.
I do not understand why it only works if I target command line the electron.exe and pass my app. OR when the app is packaged. If it's in development mode, it simply does not work.
I think? that the gotTheLock runs app.quit(), because in instances where I put the createWindow() outside of the lock if/else function, a window briefly opens and then closes immediately. For some reason it's opening a new instance completely? When createWindow() is inside, I basically see nothing happen.
I'm on Windows, I see nothing about forge that would cause issues or need extra setup in the forge config/package.json since I am on Windows
There's something fundamental flying over my head. Currently, my latest attempt is with the new 6.0 electron-forge from here with the following npm command to generate:
npm init electron-app#latest my-app --template=webpack cd my-app npm start
Here is my "main" which is called index.js here.
index.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
}
let mainWindow;
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
}
} else {
app.setAsDefaultProtocolClient('electron-fiddle')
}
const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
app.on('ready', createWindow);
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
ipcMain.on('shell:open', () => {
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
const pagePath = path.join('file://', pageDirectory, 'index.html')
shell.openExternal(pagePath)
})
I've tried so many iterations i'm honestly just lost. What's wrong with this that the electron-forge dev mode doesn't like?
As far as any other code is concerned, it's "as-is" from that npm command with the latest 6.0 electron-forge with webpack template. Only the index.js was modified

My spectron app.client doesn't contains all the methods

I'm trying to test my electron app using spectron and mocha, here is my file 'first.js' containing my tests:
const assert = require('assert');
const path = require('path');
const {Application} = require('spectron');
const electronPath = require('electron');
describe('GULP Tests', function () {
this.timeout(30000)
const app = new Application({
path: electronPath,
args: [path.join(__dirname, '..', 'main.js')]
});
//Start the electron app before each test
before(() => {
return app.start();
});
//Stop the electron app after completion of each test
after(() => {
if (app && app.isRunning()) {
return app.stop();
}
});
it('Is window opened', async () => {
const count = await app.client.getWindowCount();
return assert.equal(count, 1);
});
it('Clicks on the project creation button', async () => {
await app.client.waitUntilWindowLoaded();
const title = await app.client.
console.log(title);
return assert.equal(title, 'Welcome to GULP, !');
});
});
My first test is passing, but for the second one i'd like to do a click on an element, but my app.client does not contain a .click methods, and also no getText or getHTML. I've tried to import browser from webdriverio but it was the same problem, I get an error when testing saying me that those methods doesn't exists. I've red the spectron documentation and they're using .click and .getText methods regularly, why I don't get them ? I've imported spectron as it's said in the documentation to.
Thanks.
I have struggled with the same issue for a while. After much trial and error i changed my async methods to normal functions.
it('Clicks on the project creation button', function() {
app.client.waitUntilWindowLoaded();
const title = await app.client.
console.log(title);
return assert.equal(title, 'Welcome to GULP, !');
});
Strange but it worked for me. hopefully it helps.

how to use AppState in a functional component and hooks? Necessary or am I using useEffect incorrectly?

I want to be able to run a function when a user goes into the app. I thought useEffect would already do something like this, say if I choose to change my phone's Clipboard (copy different texts from another app). But its not rerendering the component.
const [clipped, setClipboard] = useState();
const [appView, setAppView] = useState(AppState.currentState);
const getFromClipboard = useCallback(() => {
Clipboard.getString().then(content => {
console.log('content:', content)
setClipboard(content);
});
}, [clipped]);
useEffect(() => {
getFromClipboard();
}, [getFromClipboard, clipped, appView]);
I would assume that every time I copy a new text from a different app, into my clipboard, and then I go back to this app, because the state changed in clipped, it will rerender the useEffect? Unfortunately, its not calling the console log after first initial load of the component.
I stumbled across AppState, and thought I might be able to give this a shot, but not sure how to set this up with useEffect?
You can set an event listener for app state change it will trigger when the app is closed, moved to background or foreground, You can check the documentation for more details link:
useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
}
}, []);
const handleChange = (newState) => {
if (newState === "active") {
getFromClipboard();
}
}

Electron Framework - TypeError: Cannot read property 'exec' of undefined

I am new to Electron Framework and I am trying out an application where I need to launch a particular software (e.g. firefox) on a menu item click in my app.
Right now I am trying to build the app on Windows platform (using Windows 7).
Use case
- Home page with static content
- Application menu on top with 'Launch my software' as a menu item
- On click of this menu item I want to launch a software which is installed on my system (I know the path to the exe file)
Steps to create and start the project
npm init (to enter the details of the project and create package.json)
npm install electron --save-dev --verbose
created main.js and main.html (simple HTML file with a header) as mentioned below
npm start
Contents of main.js as follows
const electron = require('electron');
const app = electron.app;
const { Menu } = require('electron');
const BrowserWindow = electron.BrowserWindow;
var childProc = electron.ChildProcess;
var mainWindow;
const menu = Menu.buildFromTemplate([
{
label: 'Menu',
submenu: [{
label: 'Launch my Software',
click: function () {
var child = childProc.exec;
var executablePath = "<path to executable for the software>";
child(executablePath, function(err, data) {
if (err) {
console.error(err);
return;
}
console.log(data.toString());
});
}
}]
}
]);
Menu.setApplicationMenu(menu);
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadURL('file://' + __dirname + '/main.html');
mainWindow.setMenu(menu);
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.on('ready', createWindow);
app.on('activate', () => {
if (mainWindow == null) {
createWindow();
}
});
After doing npm start, the project is launched. But when I click on the menu item to launch my software, I get the following error
Uncaught Exception:
TypeError: Cannot read property 'exec' of undefined
and it is pointing at line no. 18
var child = childProc.exec;
I went through different posts and tried out the suggestions but it has not worked for me. Can anyone pls help on this.
Thanks in advance.
Electron doesn't have a ChildProcess method or property.
If you want to exec a command then use;
const childProcess = require('child_process')
childProcess.exec('')

Communicate with <webview> in Electron

I have a <webview> in my Electron app. I'd like to have safe "foreign" communication, the way I would with an iframe through postMessage. So for example:
webview.executeJavaScript("window.parent.postMessage('all done!')");
Is my only choice for communication with this subwebview to turn on nodeIntegration so that I can use sendToHost? Turning on all of nodeIntegration just for this one feature seems like overkill.
You can access Electron APIs in the webview preload script, including IPC, even when nodeIntegration is disabled. Your preload script can inject functions into the global namespace that will then be accessible within the page loaded in the webview. A simple example:
webview-preload.js:
const { ipcRenderer } = require('electron')
global.pingHost = () => {
ipcRenderer.sendToHost('ping')
}
webview-index.html:
<script>
pingHost()
</script>
window-index.html:
<script>
const webview = document.getElementById('mywebview')
webview.addEventListener('ipc-message', event => {
// prints "ping"
console.log(event.channel)
})
</script>
Easiest way
Communication is
Note:
(main.js or app.js or background.js or process.js ) no need to pass (directly pass component to component),i succesffully implemented in electron:3.1.10
for print html webview.
Window To Webview
example1.html
<webview id="paper" style="width:300px;height:800px" src="file:///static/mywebview.html" nodeintegration></webview>
example1.js
var webview = document.getElementById("paper");
webview.send("ping",data);
getting data from mycomponent or window(i send directly form component)
mywebview.html
<!---what data you want show----!>
mywebview.js
const {
ipcRenderer
} = require('electron')
//data from window
ipcRenderer.on('ping', (e, data) => { console.log(data) })
webview to window
Webview to window(direct pass to component)
mywebview.js
ipcRenderer.sendToHost("readyCompanyInfo",data)
in my window eg i use vue (mycomponent.vue or mypage)
example1.html
const ipcRenderer = require("electron").ipcRenderer;
webview.addEventListener("ipc-message",(event)=>{
const {args,channel}=event;
if(channel=="readyCompanyInfo")
{
console.log(channel,args)
//here you can see data what u passed from webview to window
console.log(args[0])
}
})

Resources