I am trying to make an electron react project with my own custom frameless window.
I've accomplished this before, just without react, but not it seems like introducing react is causing some error I can't solve.
I was following this tutorial on how to add a frameless window.
And I have my electron.js file containing this code:
// Create the native browser window.
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, "preload.js"),
},
frame: false,
backgroundColor: '#FFF',
});
// In production, set the initial browser path to the local bundle generated
// by the Create React App build process.
// In development, set it to localhost to allow live/hot-reloading.
const appURL = app.isPackaged
? url.format({
pathname: path.join(__dirname, "index.html"),
protocol: "file:",
slashes: true,
})
: "http://localhost:3000";
mainWindow.loadURL(appURL);
Which loads my index.html file, inside that index.html file I have a link to renderer.js at the bottom:
<script src="renderer.js"></script>
</body>
</html>
inside renderer.js, the first lines have some require() lines
const remote = require('electron').remote;
const app = require('electron')
const win = remote.getCurrentWindow(); /* Note this is different to the
html global `window` variable */
But I havent been able to get these lines to pass correctly. my electron branch is 'frameless-window' located here:
https://github.com/MartinBarker/electron-react/tree/frameless-window
My current error message is:
node:electron/js2c/renderer_init:45 Uncaught Error: contextBridge API can only be used when contextIsolation is enabled
at node:electron/js2c/renderer_init:45
at Object.exposeInMainWorld (node:electron/js2c/renderer_init:45)
at process.<anonymous> (C:\Users\marti\Documents\projects\react-electron-oct023\electron-react\public\preload.js:9)
at Object.onceWrapper (node:events:513)
at process.emit (node:events:394)
renderer.js:4 Uncaught TypeError: Cannot read properties of undefined (reading 'getCurrentWindow')
at renderer.js:4
but if i change contextIsolation: true, in electron.js, the error changes to this:
renderer.js:1 Uncaught ReferenceError: require is not defined
Am I missing some way to make this renderer.js file work?
Related
Throughout my app I use i18n without problem. However, for email send through a cron job, I get the error:
ReferenceError: __ is not defined
In app.js I configure i18n:
const i18n = require("i18n");
i18n.configure({
locales: ["en"],
register: global,
directory: path.join(__dirname, "locales"),
defaultLocale: "en",
objectNotation: true,
updateFiles: false,
});
app.use(i18n.init);
Throughout my app I use it as __('authentication.flashes.not-logged-in'), like I said without problems. In a mail controller, that is called upon by a cron job, I use it in the same way: __('mailers.buttons.upgrade-now'). However there, and only there, it produces the mentioned error.
Just to try, I've changed this in the mail controller to i18n.__('authentication.flashes.not-logged-in'). But then I get another error:
(node:11058) UnhandledPromiseRejectionWarning: TypeError: logWarnFn is not a function
at logWarn (/data/web/my_app/node_modules/i18n/i18n.js:1180:5)
Any idea how to make the emails work that are sent through a cron job?
In comments, the asker clarified that the cron job calls mailController.executeCrons() directly, instead of making an HTTP request to the app. Because of this, the i18n global object never gets defined, because the app setup code in app.js isn't run.
The best solution would be to use i18n's instance usage. You could separate the instantiation and configuration of an I18N object out into a separate function, then call it both in app.js to set it up as Express middleware, and in the mailController.executeCrons() function to use it when being called through the cronjob.
Code outline:
i18n.js (new file)
const i18n = require("i18n");
// factory function for centralizing config;
// either register i18n for global use in handling HTTP requests,
// or register it as `i18nObj` for local CLI use
const configureI18n = (isGlobal) => {
let i18nObj = {};
i18n.configure({
locales: ["en"],
register: isGlobal ? global : i18nObj,
directory: path.join(__dirname, "locales"),
defaultLocale: "en",
objectNotation: true,
updateFiles: false,
});
return [i18n, i18nObj];
};
module.exports = configureI18n;
app.js
const configureI18n = require('./path/to/i18n.js');
const [i18n, _] = configureI18n(true);
app.use(i18n.init);
mailController.js
const configureI18n = require('./path/to/i18n.js');
const [_, i18nObj] = configureI18n(false);
executeCrons() {
i18nObj.__('authentication.flashes.not-logged-in');
}
I am using the electron framework to open a webpage with BrowserWindow.
I am using code like:
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
width: 900,
height: 800
});
win.on('closed', () => {
win = null
});
win.loadURL( "http://***.com/detail.asp?bhcp=1");
However, the opened webpage can detect my real screen resolution. What methods can I use to spoof a webpage, giving it a fake screen resolution?
thanks, i have worked out this issue.
1. set a script file to webPreferences.preload
2. on script file, run code, set window.screen properties.
that's it.
I have an Electron app that loads URL from PHP server. And the page contains an iFrame having a source to PDF. The PDF page seems absolutely ok in a normal web browser but asks for download in Electron. Any help?
My codes for html page is
<h1>Hello World!</h1>
Some html content here...
<iframe src="http://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf" width="1200" height="800"></iframe>
And my js code is something like
mainWindow = new BrowserWindow({width: 800, height: 600})
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
app.on('ready', createWindow)
Any help would be really greatful...
Electron is shipping already with an integrated PDF viewer.
So you can load PDF files just like normal HTML files, the PDF viewer will automatically show up.
E.g. in BrowserWindow with .loadURL(…), in <iframes>, <object> and also with the, at the moment discouraged, <webview>.
PS: The need to enable the plugins property in the BrowserWindow or <webview> is no more needed since Electron 9.
You will need
https://github.com/gerhardberger/electron-pdf-window
Example:
const { app } = require('electron')
const PDFWindow = require('electron-pdf-window')
app.on('ready', () => {
const win = new PDFWindow({
width: 800,
height: 600
})
win.loadURL('http://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf')
})
This answer will focus on implementation with Angular.
After year of waiting (to be solved by the Electron) finally I decided to apply a workaround. For the people who needs it done, here it goes. Workaround comes with a cost of increasing bundle size totally 500K. (For Angular)
Workaround to use Mozilla PDF.js library.
NPM
GitHub
Implementation 1 (Setting nodeIntegration: true)
This implementation has no issue, you can implement by the document of the library mentioned. But if you run into additional problem like creating white window when route is changed, it is due to the setting nodeIntegration property to true. If so, use the following implementation.
Implementation 2 (Setting nodeIntegration: false)
This is the default by Electron. Using this configuration and viewing the PDF is bit tricky. Solution is to use Uint8Array instead of a blob or base64.
You can use the following function to convert base64 to Uint8Array.
base64ToArrayBuffer(data): Uint8Array {
const input = data.substring(data.indexOf(',') + 1);
const binaryString = window.atob(input ? input : data);
const binaryLen = binaryString.length;
const bytes = new Uint8Array(binaryLen);
for (let i = 0; i < binaryLen; i++) {
const ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
}
Or convert blob to array buffer
const blob = response;
let arrayBuffer = null;
arrayBuffer = await new Response(blob).arrayBuffer();
then pass the generated Uint8Array as the pdfSource to the ng2-pdfjs-viewer.
HTML
<ng2-pdfjs-viewer zoom="100" [pdfSrc]="pdfSource"></ng2-pdfjs-viewer>
Electron 9.0.0 has enabled PDF viewer already.
npm install electron#9.0.0
Edit 2 - Final Notes
Solution below worked for me, however you still need to manually specify the mongo connection credentials as shown above for the application to run correctly, otherwise you will get a mongo auth error.
Edit 1 - Added full keystone.js
keystone.js
// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').load();
// Require keystone
var keystone = require('keystone');
var mongoDbConnectionString = process.env.OPENSHIFT_MONGODB_DB_URL || 'mongodb://localhost/********';
var host = process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1";
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.OPENSHIFT_INTERNAL_PORT || 3000;
// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.
keystone.init({
'name': '********',
'brand': '********',
'less': 'public',
'static': 'public',
'favicon': 'public/favicon.ico',
'views': 'templates/views',
'view engine': 'jade',
'emails': 'templates/emails',
'mongo': mongoDbConnectionString,
'host': host,
'port': port,
'auto update': true,
'session': true,
'auth': true,
'user model': 'User',
'cookie secret': '********'
});
// Load your project's Models
keystone.import('models');
// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js
keystone.set('locals', {
_: require('underscore'),
env: keystone.get('env'),
utils: keystone.utils,
editable: keystone.content.editable
});
// Load your project's Routes
keystone.set('routes', require('./routes'));
// Setup common locals for your emails. The following are required by Keystone's
// default email templates, you may remove them if you're using your own.
keystone.set('email locals', {
logo_src: '/images/logo-email.gif',
logo_width: 194,
logo_height: 76,
theme: {
email_bg: '#f9f9f9',
link_color: '#2697de',
buttons: {
color: '#fff',
background_color: '#2697de',
border_color: '#1a7cb7'
}
}
});
// Setup replacement rules for emails, to automate the handling of differences
// between development a production.
// Be sure to update this rule to include your site's actual domain, and add
// other rules your email templates require.
keystone.set('email rules', [{
find: '/images/',
replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/images/' : 'http://localhost:3000/images/'
}, {
find: '/keystone/',
replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/keystone/' : 'http://localhost:3000/keystone/'
}]);
// Load your project's email test routes
keystone.set('email tests', require('./routes/emails'));
// Configure the navigation bar in Keystone's Admin UI
keystone.set('nav', {
'posts': ['posts', 'post-categories'],
'creations': 'galleries',
'contact us': 'enquiries',
'users': 'users'
});
// Start Keystone to connect to your database and initialise the web server
console.log('%s: IP Set.', host);
console.log('%s: Port Set.', port);
keystone.start();
I am trying to build my first Keystone.js, got it running fine locally on my machine.
Now, i am trying to push my site to Openshift and failing miserably.
I have gotten mongo to connect by adding this to keystone.js:
var mongoDbConnectionString = process.env.OPENSHIFT_MONGODB_DB_URL || 'mongodb://localhost/*********';
However i cannot get the thing to run correctly as it seems to be having issues binding to the ip and port i am feeding it on Openshift, i am using the following code:
var host = process.env.OPENSHIFT_NODEJS_IP || process.env.OPENSHIFT_INTERNAL_IP || "127.0.0.1";
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.OPENSHIFT_INTERNAL_PORT || 3000;
Combined with:
keystone.init({
'name': '*********',
'brand': '*********',
'less': 'public',
'static': 'public',
'favicon': 'public/favicon.ico',
'views': 'templates/views',
'view engine': 'jade',
'emails': 'templates/emails',
'mongo': mongoDbConnectionString,
'host': host,
'port': port,
'auto update': true,
'session': true,
'auth': true,
'user model': 'User',
'cookie secret': '*********'
});
But i keep getting:
==> app-root/logs/nodejs.log <==
Error: listen EACCES
at errnoException (net.js:901:11)
at Server._listen2 (net.js:1020:19)
at listen (net.js:1061:10)
at Server.listen (net.js:1135:5)
....
with 'node keystone.js'
DEBUG: Sending SIGTERM to child...
Now i have checked the env on the Openshift instance and they seem to be correct variable, i am getting port 8080 and what seems like a correct IP address.
I've also tried hard coding the port and address parts but it seems to make no difference, and also not really workable for local testing.
I'm obviously missing something simple here, so help greatly appreciated!
Thanks
Edit 1 - Ignoring server.js code as per #GarethJeanne's comment
If your entry point is keystone.js and your not using server.js at all (which renders my previous answer mute), then you need to make sure you're using Keystone 0.3.3 or newer.
This issue was addressed in Pull Request 1127, which was merged on Feb. 28, and first included in version 0.3.3, which was released on Mar. 8.
I don't believe this is a Keystone issue. You're obviously not using Keystone's out-of-the-box initialization and startup, so Keystone is handling the creation or initialization of your Express app.
Since you're not sharing all of your code (e.g. self.initializeServer()), I will assume you're correctly setting up Express and connecting it to your Keystone app instance.
I will likewise assume self.app is an instance of Express.
From the code you submitted, the only problem I see is that you're passing 'self.port' and 'self.ipaddress' to self.app.listen() with single quotes around each argument.
This would definitely cause a listen EACCES error.
Try removing the single quotes surrounding 'self.port' and 'self.ipaddress' on the self.app.listen() call.
self.start = function() {
// Start the app on the specific interface (and port).
self.app.listen(self.port, self.ipaddress, function() {
console.log('%s: Node server started on %s:%d ...',
Date(Date.now() ), self.ipaddress, self.port);
});
};
I can't guarantee the rest of your app will work, but it should, at the very least, eliminate the listen EACCES error.
I have typical installation of locomotive JS. I want to use ECTJS for views. I have successfully installed ECTJS by using following command:
npm install ect --save
I have following controller:
var locomotive = require('locomotive')
, Controller = locomotive.Controller;
var roseController = new Controller();
roseController.thorne = function(){
this.render();
}
module.exports = roseController;
I have created following 'thorne.html.ect' file in directory '/app/views/rose'
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="/stylesheets/screen.css" />
</head>
<body>
<h1>Rose Controller - Thorne Action from ECT</h1>
<p></p>
</body>
</html>
I have made changes in '02_views.js' file in 'initializers' folder. File is as below:
module.exports = function() {
// Configure view-related settings. Consult the Express API Reference for a
// list of the available [settings](http://expressjs.com/api.html#app-settings).
var ECT = require('ect');
var renderer = ECT({ root : __dirname + '/app/views', ext : '.ect' });
//var renderer = ECT({ watch: true, root: __dirname + '/app/views'});
this.set('view engine', 'ect');
this.engine('ect', renderer.render);
// // this.set('views', __dirname + '/../../app/views');
// // this.set('view engine', 'ejs');
// Register EJS as a template engine.
// //this.engine('ejs', require('ejs').__express);
// Override default template extension. By default, Locomotive finds
// templates using the `name.format.engine` convention, for example
// `index.html.ejs` For some template engines, such as Jade, that find
// layouts using a `layout.engine` notation, this results in mixed conventions
// that can cause confusion. If this occurs, you can map an explicit
// extension to a format.
/* this.format('html', { extension: '.jade' }) */
// Register formats for content negotiation. Using content negotiation,
// different formats can be served as needed by different clients. For
// example, a browser is sent an HTML response, while an API client is sent a
// JSON or XML response.
/* this.format('xml', { engine: 'xmlb' }); */
}
When I run
http://localhost:3000/rose/thorne
I get following error :
500 Error: Failed to lookup view "rose/thorne.html.ect"
If I use:
this.res.send('Test');
in rose/thorne action, it shows without any problem.
Can some one guide me how can I embed ECTJS in locomotiveJS.
Thanks
It seems that you configuration in 02_views.js is broken.
Try the following configuration:
// Creating ECT renderer
var ectRenderer = ECT({
watch: false,
gzip: true,
root: __dirname + '/../../app/views'
});
// Register ECT as a template engine.
this.engine('.ect', ectRenderer.render);
// Configure application settings. Consult the Express API Reference for a
// list of the available [settings](http://expressjs.com/api.html#app-settings).
this.set('views', __dirname + '/../../app/views');
this.set('view engine', 'ect');
// Override default template extension. By default, Locomotive finds
// templates using the `name.format.engine` convention, for example
// `index.html.ect` For some template engines, such as ECT, that find
// layouts using a `layout.engine` notation, this results in mixed conventions
// that can cuase confusion. If this occurs, you can map an explicit
// extension to a format.
this.format('html', {
extension: '.ect'
});
With this configuration my view files are named filename.ect. So if filename.html.ect doesnt work you can rename it to filename.ect