I'm building an app on electron and now I'm trying to create a simple file.
Here's the JS:
const app = require("electron").remote;
var dialog = app.dialog;
var fs = require("fs");
document.getElementById('save_project').onclick=() => {
dialog.showSaveDialog((filename) => {
if(filename === undefined){
console.log("You didnt save the file");
return;
};
var content = "hello there";
fs.writeFile(filename, content, (err) => {
if(err) console.log(err);
alert("The file has been successfully saved.")
})
});
};
This window will open as supposed:
Then, I wrote for instance: "hello.txt" on the name input and clicked save.
There's no log written on the console neither a file written in the directory
Edit:
with this js, happens the same ():
const fs = require("fs");
const {dialog} = require("electron").remote;
document.getElementById("save_project").addEventListener("click", () => {
dialog.showSaveDialog((filename) => {
if(filename === undefined){
console.log("nop");
return;
}
fs.writeFile(filename, content, (err) => {
if(err){
console.log(err);
return;
}
alert("file created");
});
});
}, false);
Edited:
Here's the createWindow()
function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
nodeIntegration: true
},
});
const childWindow = new BrowserWindow ({ width: 1600, height: 800, parent: mainWindow, modal: true, show : false});
// and load the index.html of the app.
mainWindow.loadFile("index.html");
childWindow.loadFile("welcome.html");
childWindow.once("ready-to-show", () => {
childWindow.show();
});
// Open the DevTools.
mainWindow.webContents.openDevTools();
}
Basically, from what i understood, "dialog.showSaveDialog((filename).." this was kinda blocking... i fixed by using:
const {dialog} = require("electron").remote;
let filename = dialog.showSaveDialogSync()
if(filename === undefined){
console.log("filename undefined");
return;
}else{
console.log(filename)
saveAsToFile(filename, ...);
}
Related
I'm new to Electron and I joined an existing project. I'm trying to add an image before the main screen of the app that will last 5 seconds. At first, I tried it with useState and setTimeone but I've realized I'm in the wrong place of the code.
After reading all kinds of solutions, and running the app - I now need your help.
This is some of the code in my main.ts file:
mainWindow = new BrowserWindow({
show: false,
width: 521,
height: 712,
icon: getAssetPath('icon.png'),
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
preload: app.isPackaged
? path.join(__dirname, 'preload.+212 662-122618 js')
: path.join(__dirname, '../../.erb/dll/preload.js'),
},
});
mainWindow.loadURL(resolveHtmlPath('index.html'));
mainWindow.on('ready-to-show', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
mainWindow.show();
}
});
mainWindow.on('closed', () => {
mainWindow = null;
});
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
// Open urls in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
return { action: 'deny' };
});
// Remove this if your app does not use auto updates
// eslint-disable-next-line
new AppUpdater();
};
/**
* Add event listeners...
*/
app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
});
app
.whenReady()
.then(() => {
createWindow();
app.on('activate', () => {
// On macOS 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 (mainWindow === null) createWindow();
});
})
.catch(console.log);
main.js
//...
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, './preload/preload.js')
}
});
//...
preload.js
const { contextBridge, ipcRenderer } = require('electron');
const qrcode = require('qrcode');
contextBridge.exposeInMainWorld('electronAPI', {
qrc: qrcode
})
the error
Unable to load preload script: F:\project\bluetools\preload\preload.js
Error: module not found: qrcode
Although direct exposure is not a good behavior, but I need to do it; and the example in the official documentation can be exposed directly, although it is not recommended; but why I require any third-party package in preloadjs shows "module not found: xxx"
Just add nodeIntegration: true to webPreferences. Like that.
//...
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, './preload/preload.js')
}
});
//...
I refer to here, I have got a solution, but it seems to be more troublesome, but what is this compared to security and normative?
ipc.js
module.exports = {
xxx_req: (argements) => {
const { ipcMain, winb: win } = argements;
ipcMain.on('xxx_req', (event, response) => {
win.webContents.send("xxx", "hello");
});
}
}
main.js
const ipcm = require('./ipc.js');
const createWindow = () => {
const win = new BrowserWindow({...});
ipcm.xxx_req({ ipcMain, winb: win })
}
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
xxx_req: (response) => ipcRenderer.send('xxx_req',response),
xxx: (callback) => ipcRenderer.on('xxx',callback),
})
index.js(render process)
window.electronAPI.xxx_req("hello");
window.electronAPI.qrcode((event, detail) => {
console.log(detail)
});
If you still don't understand why I did this, please refer to here.
Running into a really strange quirk and can't figure out what I'm doing wrong:
init.js
const os = require('os');
const path = require('path');
const { app, BrowserView, BrowserWindow, ipcMain } = require('electron');
let win = null;
// Init function
const init = function() {
return new Promise((resolve, reject) => {
try {
// BrowserWindow config
const config = {
backgroundColor: '#1d1e20',
show: false,
webPreferences: {
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js'),
}
};
// Make BrowserWindow frameless (win) or hide titlebar (mac)
if ('win32' === os.platform()) {
config.frame = false;
} else if ('darwin' === os.platform()) {
config.titleBarStyle = 'hidden';
}
// Create BrowserWindow
win = new BrowserWindow(config);
// Add listener for BrowserWindow 'ready-to-show' event
win.once('ready-to-show', () => {
// Set traffic light position (mac)
win.setTrafficLightPosition({ x: 10, y: 27 });
// Show browser window
win.show();
// Open DevTools
win.openDevTools({ mode: "detach" });
resolve();
});
// Load app html
win.loadFile('./app.html').then(() => {
// loaded
}).catch((err) => {
reject(err);
});
} catch (err) {
reject(err);
}
});
};
// Add browser view
const addView = function(url) {
return new Promise((resolve, reject) => {
try {
// Get window size
const bounds = win.getSize();
// Create BrowserView
const view = new BrowserView({
backgroundColor: "#ffffff",
webPreferences: {
contextIsolation: true,
enableRemoteModule: false,
//preload: path.join(__dirname, 'preload.js'),
}
});
view.setBounds({
height: 375,
width: 375,
x: 0,
// Set "y" coordinate to 0
// (should be relative to BrowserWindow 0, 0)
y: 0
// Set "y" coordinate to a
// negative integer
//y: -200
// Instead, set "y" to inverse of
// BrowserWindow height
//y: bounds[1] * -1
});
// Load file
view.webContents.loadFile('./new.html').then(() => {
// Add to BrowserWindow
win.addBrowserView(view);
// Open DevTools
view.webContents.openDevTools({ mode: "detach" });
resolve();
}).catch((err) => {
reject(err);
});
} catch (err) {
reject(err);
}
});
};
// init when ready
app.whenReady().then(() => {
init().then(() => {
addView().then(() => {
console.log("Everything should be working right");
}).catch((err) => {
console.log("Error loading BrowserView");
console.error(err);
});
}).catch((err) => {
console.log("Error loading BrowserWindow");
console.error(err);
});
});
Expected result: new BrowserView stacked squarely on top of BrowserWindow at defined width and height, positioned at 0, 0.
Actual result: new BrowserView displayed below the fold, with its "y" coordinate relative to bottom of BrowserWindow. To get BrowserView to show at BrowserWindow 0, 0, must set BrowserView "y" coordinate to (BrowserWindowHeight * -1)
Edit: P.S.
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "init.js",
"scripts": {
"start": "electron ."
},
"author": "",
"license": "",
"devDependencies": {
"electron": "^12.0.1"
}
}
preload.js
(no contents)
app.html
(no contents)
app.js
(no contents)
app.css
(no contents)
new.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<style>
html {
margin: 0;
padding: 0;
}
body {
background: #ffffff;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
Update #1
Delay call to view.setBounds() by moving inside promise callback of view.loadURL(). No change.
Update #2
Further delay call to view.setBounds() by additionally wrapping in setTimeout() with delay of 1000ms. No change.
Update #3
By adding the following, the BrowserView does snap to correct 0, 0 position within parent BrowserWindow, but only upon resize event:
win.on('will-resize', (event, newBounds) => {
win.getBrowserViews().forEach((view) => {
view.setBounds({
height: newBounds.height,
width: newBounds.width,
x: 0,
y: 0
});
});
});
Update #4
Replaced BrowserWindow.loadFile() and BrowserView.loadFile() calls with .loadURL() using google.com and stackoverflow.com, respectively, to eliminate the possibility of it being something to do with my local files. No change.
Update #5
By modifying the init.js as follows, it works as expected, however it's not pretty as it "snaps" into position. (Edit: to clarify, BrowserView defined with solid background which I expect to be displayed until .loadURL() completes, unless I've somehow misunderstood the purpose of the backgroundColor property) (Edit: I've just realized that backgroundColor is not a valid property of the BrowserView)
const os = require('os');
const path = require('path');
const { app, BrowserView, BrowserWindow, ipcMain } = require('electron');
let win = null;
// Init function
const init = function() {
return new Promise((resolve, reject) => {
try {
// BrowserWindow config
const config = {
backgroundColor: '#1d1e20',
show: false,
webPreferences: {
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js'),
}
};
// Make BrowserWindow frameless (win) or hide titlebar (mac)
if ('win32' === os.platform()) {
config.frame = false;
} else if ('darwin' === os.platform()) {
config.titleBarStyle = 'hidden';
}
// Create BrowserWindow
win = new BrowserWindow(config);
// Add listener for BrowserWindow 'ready-to-show' event
win.once('ready-to-show', () => {
// Set traffic light position (mac)
win.setTrafficLightPosition({ x: 10, y: 27 });
// Show browser window
win.show();
// Open DevTools
win.openDevTools({ mode: "detach" });
});
// Add listener for BrowserWindow 'show' event
win.once('show', () => {
resolve();
});
win.on('will-resize', (event, newBounds) => {
win.getBrowserViews().forEach((view) => {
view.setBounds({ height: newBounds.height, width: newBounds.width, x: 0, y: 0 });
});
});
// Load app html
win.loadURL("https://google.com").then(() => {
// loaded
}).catch((err) => {
reject(err);
});
} catch (err) {
reject(err);
}
});
};
// Add browser view
const addView = function(url) {
return new Promise((resolve, reject) => {
try {
// Create BrowserView
const view = new BrowserView({
backgroundColor: "#edeef0",
webPreferences: {
contextIsolation: true,
enableRemoteModule: false
}
});
// Load file
view.webContents.loadURL('https://stackoverflow.com').then(() => {
setTimeout(() => {
// Get window size
const bounds = win.getSize();
// Set BrowserView bounds
view.setBounds({ height: bounds[1], width: bounds[0], x: 0, y: 0 });
}, 1);
// Add to BrowserWindow
win.addBrowserView(view);
// Open DevTools
view.webContents.openDevTools({ mode: "detach" });
resolve();
}).catch((err) => {
reject(err);
});
} catch (err) {
reject(err);
}
});
};
// init when ready
app.whenReady().then(() => {
init().then(() => {
addView().then(() => {
console.log("Everything should be working right");
}).catch((err) => {
console.log("Error loading BrowserView");
console.error(err);
});
}).catch((err) => {
console.log("Error loading BrowserWindow");
console.error(err);
});
});
Update #6
I've come to the conclusion that in current version of Electron (12.0.1), a BrowserView is rendered with equivalent of CSS position: relative, but almost immediately thereafter, changed to equivalent of CSS position: absolute. Calling BrowserView.setBounds({ x: 0, y: 0 }) immediately upon creation of the BrowserView, the BrowserView is actually displayed at { x: 0, y: (BrowserWindow height) }, whereas calling BrowserView.setBounds({ x: 0, y: 0 }) within a setTimeout() after a 1ms delay, the BrowserView is actually displayed at { x: 0, y: 0}. I'm not sure why this is, but it seems the fix (tested only on macOS 10.14.6) is as follows:
const view = new BrowserView(...);
const size = win.getSize();
view.setBounds({ height: size[1], width: size[0], x: 0, y: size[0] * -1 });
and, on the BrowserWindow:
win.on('will-resize', (event, newBounds) => {
win.getBrowserViews().forEach((view) => {
view.setBounds({ height: newBounds.height, width: newBounds.width, x: 0, y: 0 });
});
});
Update #7
This still feels very unwieldy and I suspect this is not the intended behavior and I still may be doing something wrong. I'm going to leave this question open and hope that someone has a better solution than what I've found.
Update #8
Finally figured out that it was my sloppy implementation. After RTFM, adding some promises, and doing things in the proper order, there is no problem.
For educational purposes, I broke it down and reversed my steps to find the source of the problem so I know for future reference. For anyone else who may ever find this and have the same problem -- you must call BrowserWindow.addBrowserView() BEFORE calling BrowserView.setBounds(). That was the problem.
I was calling BrowserView.setBounds() immediately after creating the BrowserView -- at which point it was not added to the BrowserWindow yet, hence why the coords were off. I didn't call BrowserWindow.addBrowserView() until BrowserView.loadFile() resolved.
Must call BrowserWindow.addBrowserView() BEFORE calling BrowserView.setBounds() -- (see Update #8)
I'm trying to make my electron app auto-updatable, and after searching on google I found nice guide that works. The only problem is that I want to create a button for when update is downloaded so that the user can decide when he wants to update and restart the app. So far I was able to put this code together
renderer.js
const electron = require('electron')
const ipcRenderer = electron.ipcRenderer
let lastMsgId = 0
window.quitAndInstall = function () {
electron.remote.autoUpdater.quitAndInstall()
}
ipcRenderer.on('console', (event, consoleMsg) => {
console.log(consoleMsg)
})
ipcRenderer.on('message', (event, data) => {
showMessage(data.msg, data.hide, data.replaceAll)
})
function showMessage(message, hide = true, replaceAll = false) {
const messagesContainer = document.querySelector('.messages-container')
const msgId = lastMsgId++ + 1
const msgTemplate = `<div id="${msgId}" class="alert alert-info alert-info-message animated fadeIn">${message}</div>`
if (replaceAll) {
messagesContainer.innerHTML = msgTemplate
} else {
messagesContainer.insertAdjacentHTML('afterbegin', msgTemplate)
}
if (hide) {
setTimeout(() => {
const msgEl = document.getElementById(msgId)
msgEl.classList.remove('fadeIn')
msgEl.classList.add('fadeOut')
}, 4000)
}
}
and this is my index.js where messages are storred
const electron = require('electron');
const {autoUpdater} = require('electron-updater');
const log = require('electron-log');
const appVersion = require('./package.json').version
// configure logging
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
log.info('App starting...');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1020,
height: 800,
});
mainWindow.loadURL('file://' +__dirname + '/public/index.html');
// Open the DevTools.
//mainWindow.webContents.openDevTools();
mainWindow.on('closed', function() {
mainWindow = null;
});
}
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function() {
app.quit();
});
app.on('activate', function() {
// 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 (mainWindow === null) {
createWindow();
}
});
//-------------------------------------------------------------------
// Auto updates
//-------------------------------------------------------------------
const sendStatusToWindow = (text) => {
log.info(text);
if (mainWindow) {
mainWindow.webContents.send('console', `App version: ${appVersion}`)
mainWindow.webContents.send('message', { msg: `App version: ${appVersion}` })
}
};
autoUpdater.on('error', (ev, err) => {
mainWindow.webContents.send('message', { msg: `Error: ${err}` })
})
autoUpdater.once('checking-for-update', (ev, err) => {
mainWindow.webContents.send('message', { msg: 'Checking for updates' })
})
autoUpdater.once('update-available', (ev, err) => {
mainWindow.webContents.send('message', { msg: 'Update available. Downloading ⌛️', hide: false })
})
autoUpdater.once('update-not-available', (ev, err) => {
mainWindow.webContents.send('message', { msg: 'Update not available' })
})
autoUpdater.once('update-downloaded', (ev, err) => {
const msg = 'Update downloaded - <button onclick="quitAndInstall()">Restart</button>'
mainWindow.webContents.send('message', { msg, hide: false, replaceAll: true })
})
autoUpdater.checkForUpdates()
As you can see I added a button to call for function but it doesnt work. When I press the button nothing happens. If I remove button and just say auto.updater.quitAndInstall() it works. It auto close window and install new version. What am I missing?
I think it's that electron.remote.autoUpdater.quitAndInstall() doesn't work when run in the renderer.
In the docs it doesn't say anything against running it in the renderer process but I think sending a message back to the main process to run the quitAndInstall function would work.
Inside of the quitAndInstall function put this instead:
ipcRenderer.send('asynchronous-message', 'quitAndInstall');
Then in the main process put:
electron.ipcMain.on('asynchronous-message', function (evt, message) {
if (message == 'quitAndInstall') {
autoUpdater.quitAndInstall();
}
});
I'm currently trying to implement a new Window on my Electron App.
So I want to include a button, and when you click on this Button, a new Window should be opened.
I didn't find anything in the Electron documentation, maybe one of you can help me.
Perhaps something like this:
const button = document.getElementById('<your_button_id>');
button.addEventListener('click', () => {
createBrowserWindow();
});
function createBrowserWindow() {
const remote = require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
const win = new BrowserWindow({
height: 600,
width: 800
});
win.loadURL('<url>');
}
I believe the answer that has been taken as correct is outdated.
I have managed to do it with the ipc module and with the solution given by Nishkal.
Please, read the ipc module, I'm new to electron and not very experienced with programming. I'm sure you can come with better solutions.
Code I added in order for it to work:
my main.js
const {app, BrowserWindow} = require('electron');
const path = require('path');
//ipc
const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
createWindow();
})
const 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();
})
my preload.js
window.addEventListener('DOMContentLoaded', () => {
const { ipcRenderer } = require('electron')
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg) // prints "pong"
})
//button and its event listener
const b1 = document.getElementById('b1');
b1.addEventListener('click', () => {
ipcRenderer.send('asynchronous-message', 'ping')
})
})
To open a window from the renderer:
window.open("https://github.com", "_blank", "top=500,left=200,frame=false,nodeIntegration=no");
https://www.electronjs.org/docs/latest/api/window-open