electron app json data to renderer using ipc - javascript

i am new to electron so im sorry if my question is childish
but i have sent some user inout data as object from renderer to main to store in json file (electron-db)
but i want to show all data in a table
i tired this by event.reply but it giving my desired result in preload's log only
i dont know how to take it from preload to html table
here i have attached my code from main
// catch obj This is Main.js
ipcMain.on("message", (event, obj) => {
db.createTable("medicine", (succ, msg) => {
// succ - boolean, tells if the call is successful
if (succ) {
console.log(msg);
} else {
console.log("An error has occured. " + msg);
}
});
console.warn(obj);
if ((db.valid("medicine"), location)) {
db.insertTableContent("medicine", obj, (succ, msg) => {
// succ - boolean, tells if the call is successful
console.log("Success: " + succ);
console.log("Message: " + msg);
});
}
let row;
const app = electron.app || electron.remote.app;
db.getAll("medicine", (succ, data) => {
// succ - boolean, tells if the call is successful
// data - array of objects that represents the rows.
// console.warn(data);
row = data;
});
console.warn(row)
event.reply("message", row)
});
and this is my preload
const {contextBridge, ipcMain, ipcRenderer} = require('electron');
const API = {
sendmsg: (obj) => ipcRenderer.send("message", obj)
}
// const getAllData = function fetchData(){
// ipcRenderer.send("req", "this is requirment for All data")
// }
ipcRenderer.on("message", (_event, row) => {
console.log(row)
})
contextBridge.exposeInMainWorld("api", API);
consol.log(row) giving my an array of all data in my db
but i dont know how to make it availabe in html files script or in renderer

The wiring of your IPC is a bit mixed up and confusing. To simplify your scripts, try separating them into their own functionality. IE: Use your preload.js script to allow communication between the main process and render process(es) only.
Not knowing what database you are using, I will assume your database code is correct and functional.
Below is an example of how I would set-up my preload.js script and use it to communicate between processes.
This preload.js script uses whitelisted channel names to identify which IPC lines of communication can be used, similar to Node.js eventName.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'message' // Channel name
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
For brevity, I have included your database code within the below main.js file.
main.js (main process)
'use strict';
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// db = require('db'); // Your (connected) database
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
// From render to main and back again
electronIpcMain.handle('message', (event, obj) => {
// Create the table
db.createTable("medicine", (succ, msg) => {
if (succ) {
console.log(msg);
} else {
console.log('An error has occurred. ' + msg);
}
});
console.warn(obj);
// Insert your data object
if ((db.valid("medicine"), location)) {
db.insertTableContent("medicine", obj, (succ, msg) => {
console.log('Success: ' + succ);
console.log('Message: ' + msg);
});
}
let rows;
// Retrieve all rows
db.getAll("medicine", (succ, data) => {
// succ - boolean, tells if the call is successful
// data - array of objects that represents the rows.
// console.warn(data);
rows = data;
});
console.warn(rows);
return rows; // Simple return the result via IPC
// Testing purposes.
// let rows = [
// {"name": "Amoxicillin", "weight": "250mg"},
// {"name": "Codeine", "weight": "50mg"},
// {"name": "Penicillin", "weight": "25mg"}
// ];
//
// rows.push(obj);
//
// console.log(rows);
//
// return rows;
})
Lastly, I have tried to implement what the basic functionality of your index.html file may be like.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Medicine</title>
</head>
<body>
<h1>Add Medicine</h1>
<label for="name">Name: </label>
<input id="name" type="text">
<label for="weight">Weight: </label>
<input id="weight" type="text">
<input id="button" type="button" value="Add">
</body>
<script>
let name = document.getElementById('name');
let weight = document.getElementById('weight');
document.getElementById('button').addEventListener('click', () => {
// From render to main and (.then) back again
window.ipcRender.invoke('message', {"name": name.value, "weight": weight.value})
.then((rows) => {
console.log(rows);
})
})
</script>
</html>

Related

Broadcasting to all clients with Deno websocket

I want to add notifications to an application I've developed.
Unfortunately, Deno has removed the ws package.(https://deno.land/std#0.110.0/ws/mod.ts)
That's why I'm using the websocket inside the denon itself. Since it doesn't have many functions, I have to add some things myself.
For example, sending all messages to open clients.
What I want to do is when the pdf is created, a (data, message) comes from the socket and update the notifications on the page according to the incoming data.
I keep all open clients in a Map. and when the pdf is created, I return this Map and send it to all sockets (data, message).
However, this works for one time.
server conf...
import {
path,
paths,
ctid,
} from "../deps.ts";
const users = new Map();
const sockets = new Map()
const userArr = [];
export const startNotif = (socket,req) => {
const claims = req.get("claims");
const org = req.get("org");
claims.org = org;
console.log("connected")
users.set(claims.sub, {"username":claims.sub,"socket":socket})
users.forEach((user)=>{
if(userArr.length === 0){
userArr.push(user)
}
else if(userArr.every((w)=> w.username !== user.username) )
userArr.push(user)
})
sockets.set(org, userArr)
function broadcastMessage(message) {
sockets.get(org).map((u)=>{
console.log(u.socket.readyState)
u.socket.send(message)
})
}
if (socket.readyState === 3) {
sockets.delete(uid)
return
}
const init = (msg) => {
socket.send(
JSON.stringify({
status: "creating",
})
);
};
const ondata = async (msg) => {
const upfilepath = path.join(paths.work, `CT_${msg.sid}_report.pdf`);
try {
const s=await Deno.readTextFile(upfilepath);
if(s){
socket.send(
JSON.stringify({
status: "end",
})
);
} else {
socket.send(
JSON.stringify({
status: "creating",
})
);
}
} catch(e) {
if(e instanceof Deno.errors.NotFound)
console.error('file does not exists');
}
};
const end = () => {
try {
const endTime = Date.now()
const msg = "Your PDF has been created"
const id = ctid(12) // random id create
broadcastMessage(
JSON.stringify({
id: id,
date: endTime,
status: "done",
message: msg,
read: 'negative',
action: 'pdf'
})
);
} catch (e) {
console.log(400, "Cannot send.", e);
}
}
socket.onmessage = async (e) => {
const cmd = JSON.parse(e.data);
if(cmd.bid === 'start'){
await init(cmd)
}
if(!cmd.bid && cmd.sid){
await ondata(cmd)
}
if(cmd.bid === 'end'){
await end();
}
}
socket.onerror = (e) => {
console.log(e);
};
}
client conf...
export const webSocketHandler = (request) =>
new Promise((res, rej) => {
let url;
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
url = `http://localhost:8080/api/notifications/ws`.replace('http', 'ws');
} else {
url = `${window.location.origin}/api/notifications/ws`.replace('http', 'ws');
}
const token = JSON.parse(sessionStorage.getItem('token'));
const orgname = localStorage.getItem('orgname');
const protocol = `${token}_org_${orgname}`;
const socket = new WebSocket(url, protocol);
const response = Object.create({});
socket.onopen = function () {
socket.send(
JSON.stringify({
bid: 'start',
})
);
};
socket.onmessage = function (event) {
response.data = JSON.parse(event.data);
if (response.data.status === 'creating') {
socket.send(
JSON.stringify({
sid: request.sid,
})
);
} else if (response.data.status === 'end') {
socket.send(
JSON.stringify({
bid: 'end',
})
);
} else if (response.data.status === 'done') {
try {
res(response);
} catch (err) {
rej(err);
}
}
};
socket.onclose = function (event) {
response.state = event.returnValue;
};
socket.onerror = function (error) {
rej(error);
};
});
onclick function of button I use in component...
const donwloadReport = async (type) => {
const query = `?sid=${sid}&reportType=${type}`;
const fileName = `CT_${sid}_report.${type}`;
try {
type === 'pdf' && setLoading(true);
const response = await getScanReportAction(query);
const request = {
sid,
};
webSocketHandler(request)
.then((data) => {
console.log(data);
dispatch({
type: 'update',
data: {
id: data.data.id,
date: data.data.date,
message: data.data.message,
action: data.data.action,
read: data.data.read,
},
});
})
.catch((err) => {
console.log(err);
});
if (type === 'html') {
downloadText(response.data, fileName);
} else {
const blobUrl = await readStream(response.data);
setLoading(false);
downloadURL(blobUrl, fileName);
}
} catch (err) {
displayMessage(err.message);
}
};
Everything works perfectly the first time. When I press the download button for the pdf, the socket works, then a data is returned and I update the notification count with the context I applied according to this data.
Later I realized that this works in a single tab. When I open a new client in the side tab, my notification count does not increase. For this, I wanted to keep all sockets in Map and return them all and send a message to each socket separately. But in this case, when I press the download button for the second time, no data comes from the socket.
Actually, I think that I should do the socket initialization process on the client in the context. When you do this, it starts the socket 2 times in a meaningless way.
In summary, consider an application with organizations and users belonging to those organizations. If the clients of A, B, C users belonging to X organization are open at the same time and user A pressed a pdf download button, I want A, B, C users to be notified when the pdf is downloaded.
I would be very grateful if someone could show me a way around this issue.
Have you looked at the BroadcastChannel API? Maybe that could solve your issue. See for example:
Deno specific: https://medium.com/deno-the-complete-reference/broadcast-channel-in-deno-f76a0b8893f5
Web/Browser API: https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

Why electron defaultPath is not opening the provided directory path

Hi i'm not able to open the provided directory path(/home/userxyz/Releases/alpha) with electron
What i'm trying
i have a path like this on ubuntu /home/userxyz/Releases/alpha
when i try to open this path with below code
const files = await dialog.showOpenDialog({
title:'Browse mapped drive',
defaultPath: "/home/userxyz/Releases/alpha",
properties: ['openFile', 'multiSelections']
});
it is opening some junk directory
Note: i will be having variable let openDirectory = "/home/userxyz/Releases/alpha" which will change based on config value.
Question: i want to open a directory with the provided path let say the path will be /home/userxyz/Releases/alpha
Regarding successfully opening a defaultPath, I have noticed that if the passed-in path does not exist, then either
the last opened path or the valid part of the defaultPath (starting from the left) is opened. Therefore, you would want to confirm that
the defaultPath exists prior to opening a dialog.
I have included a full but minimised example on how to implement your desired action. Note the use of the preload.js
script (via whitelisted channel names only) and
the ipcRenderer.invoke(channel, ...args)
method in the render process and
the ipcMain.handle(channel, listener)
method in the main process.
I have used the channel name openFileDialog.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'openFileDialog' // Channel name
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
See below for further use of the preload.js script.
/**
* Render --> Main
* ---------------
* Render: window.ipcRender.send('channel', data); // Data is optional.
* Main: electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main: windowName.webContents.send('channel', data); // Data is optional.
* Render: window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', async (event, data) => {
* return await promiseName(data)
* .then(() => { return result; })
* });
*/
In this main.js file, one would want to check that the defaultPath exists first, else when the dialog
opens it may be unreliable / appear as a random path.
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
// Only pass in a valid defaultPath
let defaultPath = '/home/user/Releases/alpha'; // Ubuntu
// let defaultPath = 'C:\\Users\\user\\invalid\\path'; // Windows
electronIpcMain.handle('openFileDialog', () => {
// Dialog options
let options = {
title: 'Browse mapped drive',
defaultPath: defaultPath,
properties: ['openFile', 'multiSelections']
};
// Open dialog
return electronDialog.showOpenDialog(window, options)
.then((result) => {
// Bail early if user cancelled dialog
if (result.canceled) { return }
return result.filePaths;
})
})
Detect button click, send an IPC message to the main thread which opens the dialog. Upon the dialog returning a value,
it is returned to the render process and displayed as <li> items.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<input type="button" id="button" value="Open File Dialog">
<ul id="paths"></ul>
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
window.ipcRender.invoke('openFileDialog')
.then((paths) => {
if (paths === undefined) { return } // Dialog was cancelled
let result = '';
for (let path of paths) {
result += '<li>' + path + '</li>';
}
document.getElementById('paths').innerHTML = result;
})
})
</script>
</html>

Having trouble adding a callback function from the main.js to the renderer.js after button click in another window

Hi at the moment i have used the ipcRenderer in one of my JS files to execute a function in the main.js. This code executes without a problem. What I'm trying to do straight after is send a callback function to the main renderer.js file so that i can execute another function which manipulates the appearance of some icons in the main window.
main.js code:
ipcMain.on('ReadyBtn', (event) => {
console.log('request received')
ToggleReady();
event.returnValue = "toggle ready test";
})
sidebar.js code - this file is where I'm using the ipc renderer:
const ReadyBtn = document.getElementById('btnReady')
ReadyBtn.addEventListener('click', function(){
ipcRenderer.sendSync("ReadyBtn")
});
The code above works fine, when the button is clicked the toggleReady() function within the main.js file is triggered.
What i would now like to do is set up a call back function in the renderer.js file so that i can execute another function "stateReady()", after the button is clicked. Below is what i have so far:
(async () =>{
await ipcRenderer.invoke('ReadyBtn')
stateReady();
})();
Noting that you have not included your preload.js script in your question, I have added the below preload.js script
which has no concrete implementations in it (apart from the IPC methods), only configuration of whitelisted channel names.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'ReadyBtn' // Channel name
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
This preload.js script is used like so.
/**
* Render --> Main
* ---------------
* Render: window.ipcRender.send('channel', data); // Data is optional.
* Main: electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main: windowName.webContents.send('channel', data); // Data is optional.
* Render: window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', async (event, data) => {
* return await promiseName(data)
* .then(() => { return result; })
* });
*/
In your main.js file you will use the handle method to listen for a message on the ReadyBtn channel, process the request and then return a response.
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
electronIpcMain.handle('ReadyBtn', (event, message) => {
console.log('Request received'); // Testing
// toggleReady();
return 'Toggle ready test';
})
In your index.html file, you will use the invoke method to send a message on the ReadyBtn channel to the main process and then wait for a response.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
</head>
<body>
<input id="button" type="button" value="Button">
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
window.ipcRender.invoke('ReadyBtn')
.then((result) => {
console.log(result); // Testing
console.log('Request received'); // Testing
// stateReady();
})
})
</script>
</html>

Node/Express service, span generated in logs but can not see my service in Jaeger UI

I am trying to setup Jaeger tracing for my micro service that is written in Node.js using Express.js.
I have added a simple get request handler in my express app and when I hit the endpoint via curl, I can see that a span is generated in logs, but I do not see the name of my service in Jaeger UI.
// server.js
const initTracer = require('./tracing').initTracer;
const {Tags, FORMAT_HTTP_HEADERS} = require('opentracing')
const tracer = initTracer('demo-service')
app.get('/service/model/jaeger', function(req, res) {
var requestEndpoint = req.originalUrl
console.log('[services.server.js] GET received on:', requestEndpoint)
const parentSpanContext = tracer.extract(FORMAT_HTTP_HEADERS, req.headers)
const span = tracer.startSpan('http_get_' + requestEndpoint, {
childOf : parentSpanContext,
tags : {[Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_SERVER}
});
span.log({'event': `http_get_${requestEndpoint}_end`, 'value' : 'some_value_here'});
span.finish()
res.send('Hello Jaeger bro!')
})
// tracing.js
'use strict'
var initJaegerTracer = require('jaeger-client').initTracer
function initTracer(serviceName) {
var config = {
'serviceName': serviceName,
'sampler': {
'type': 'const',
'param': 1
},
'reporter': {
'logSpans': true,
}
}
var options = {
'logger': {
'info': function logInfo(msg) {
console.log('INFO ', msg)
},
'error': function logError(msg) {
console.log('ERROR', msg)
}
}
}
const tracer = initJaegerTracer(config, options)
//hook up nodejs process exit event
process.on('exit', () => {
console.log('flush out remaining span')
tracer.close()
})
//handle ctrl+c
process.on('SIGINT', () => {
process.exit()
})
return tracer
}
exports.initTracer = initTracer
I see in logs:
2018-03-08T01:03:34.519134479Z INFO Reporting span 9b88812951bcd52f:9b88812951bcd52f:0:1
I finally figured this out after trying out different combinations. This is happening because Jaeger agent is not receiving any UDP packets from my application.
You need to tell the tracer where to send UDP packets, which in this case is docker-machine ip
I added:
'agentHost': '192.168.99.100',
'agentPort': 6832
and then I was able to see my services in Jaeger UI.
function initTracer(serviceName, options) {
var config = {
'serviceName': serviceName,
'sampler': {
'type': 'const',
'param': 1
},
'reporter': {
'logSpans': true,
'agentHost': '192.168.99.100',
'agentPort': 6832
}
}
var options = {
'logger': {
'info': function logInfo(msg) {
console.log('INFO ', msg)
},
'error': function logError(msg) {
console.log('ERROR', msg)
}
}
}
const tracer = initJaegerTracer(config, options)
//hook up nodejs process exit event
process.on('exit', () => {
console.log('flush out remaining span')
tracer.close()
})
//handle ctrl+c
process.on('SIGINT', () => {
process.exit()
})
return tracer
}
exports.initTracer = initTracer

Can't pass an IPC message to a browserwindow

I'm messing around with electron, one of the things i'm trying to do is monitor a directory for file changes, and send the full path of the file that changed to a browser window via IPC to a renderer. I know the file watcher is working since it's logging to the console, but the path never makes it to the browserwindow.
Putting logging lines in the renderer never log, and remote debugging and stepping through the code, i can see it never fires off to the renderer.
Any idea? I'm new to this javascript stuff so it's probably something simple i'm missing. Thanks in advance!
main.js:
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const ipc = electron.ipcMain
const watchfiles = require('./watchfiles')
const windows = []
app.on('ready', _ => {
[1].forEach(_ => {
let win = new BrowserWindow({
height: 400,
width: 400
})
win.loadURL(`file://${__dirname}/watchfiles.html`)
win.on('closed', _ => {
mainWindow = null
})
windows.push(win)
})
})
ipc.on('filewatch-start', _ => {
//Log
console.log('Filewatch started')
watchfiles(fullPath => {
windows.forEach(win => {
win.webContents.send('changedfiles', fullPath)
})
})
})
renderer.js:
const electron = require('electron')
const ipc = electron.ipcRenderer
document.getElementById('start').addEventListener('click', _ =>{
ipc.send('filewatch-start')
})
ipc.on('changedfiles', (event, message) => {
document.getElementById('changedfiles').innerHTML = message
})
watchfiles.js:
module.exports = function watchfiles(watchr) {
// Import the watching library
var watchr = require('watchr')
// Define our watching parameters
var path = process.cwd()
function listener (changeType, fullPath, currentStat, previousStat) {
switch ( changeType ) {
case 'update':
console.log('the file', fullPath, 'was updated', currentStat, previousStat)
break
case 'create':
console.log('the file', fullPath, 'was created', currentStat)
break
}
}
function next (err) {
if ( err ) return console.log('watch failed on', path, 'with error', err)
console.log('watch successful on', path)
}
// Watch the path with the change listener and completion callback
var stalker = watchr.open(path, listener, next)
// Close the stalker of the watcher
//stalker.close()
}
watchfile.html:
<html>
<head>
<link rel="stylesheet" href="watchfiles.css" />
</head>
<body>
<div id="container">
<h1 class="title">Watching files:</h1>
<div class="watchfiles" id="changedfiles"></div>
<button class="btn" id="start">Start</button>
</div>
<script>require('./renderer')</script>
</body>
</html>
Regarding only the IPC communication this simpler version works:
main.js
const { app, ipcMain, BrowserWindow } = require('electron')
const windows = []
app.on('ready', _ => {
/* ... */
})
ipcMain.on('filewatch-start', _ => {
console.log('Filewatch started')
windows.forEach(win => {
win.webContents.send('changedfiles', 'examplePath')
})
})
renderer.js
const { ipcRenderer } = require('electron')
window.onload = () => {
document.getElementById('start').addEventListener('click', _ =>{
ipcRenderer.send('filewatch-start')
})
ipcRenderer.on('changedfiles', (event, message) => {
document.getElementById('changedfiles').innerHTML = message
})
}
watchfile.html
<html>
<head>
<script type="text/javascript" src="./renderer.js"></script>
</head>
<body>
<div id="container">
<h1 class="title">Watching files:</h1>
<div class="watchfiles" id="changedfiles" />
<button class="btn" id="start">Start</button>
</div>
</body>
</html>
Regarding the overall design, you can do the job of watchfiles.js in main.js, no need to separate them:
main.js (compact)
const { app, ipcMain, BrowserWindow } = require('electron')
const watchr = require('watchr')
const path = process.cwd()
let windows = []
let stalker = null
const listener = (changeType, fullPath, currentStat, previousStat) => {
var msg = ''
console.log('change detected: ', changeType, currentStat, previousStat)
switch ( changeType ) {
case 'update':
msg = 'the file' + fullPath + ' was updated'
break
case 'create':
msg = 'the file' + fullPath + ' was created'
break
}
windows.forEach(win => {
win.webContents.send('changedfiles', msg)
})
}
const next = (err) => {
if ( err ) return console.log('watch failed on', path, 'with error', err)
console.log('watch successful on', path)
}
app.on('ready', () => {
[1].forEach(() => {
let win = new BrowserWindow({
height: 400,
width: 400
})
win.loadURL(`file://${__dirname}/watchfile.html`)
win.on('closed', _ => {
mainWindow = null
})
windows.push(win)
})
})
app.on('quit', () => {
if (stalker) stalker.close()
})
ipcMain.on('filewatch-start', _ => {
console.log('Filewatch started')
if (!stalker) {
stalker = watchr.open(path, listener, next)
}
})

Categories

Resources