Electron "ready-to-show" event not working as expected - javascript

Here is a block of code from my application Codey.
src/main.py
// Show window once it has finished initialising
docsWindow.once("ready-to-show", () => {
if (darkMode) {
docsWindow.webContents.send("dark-mode:toggle");
}
if (!isDarwin) {
docsWindow.webContents.send("platform:not-darwin");
}
docsWindow.webContents.send("docs:jump", section);
docsWindow.show();
});
src/docs/renderer.js
window.api.darkMode.toggle.receive(toggleDarkMode);
When darkMode = true, toggleDarkMode is never run.
My application has two different windows - an editor and a docs window. For both windows, on the "ready-to-show" event, "dark-mode:toggle" is sent to the renderer process. However, the docs window fails to run the toggleDarkMode function whilst it works for the editor window.
Note: The application must be packaged using "yarn package" as some features do not work in the dev environment.
Any help will be much appreciated.
(Repo: https://github.com/Liamohara/Codey)

For anyone that's interested. I found a solution to my issue.
src/main.py
// Show window once it has finished initialising
- docsWindow.once("ready-to-show", () => {
+ docsWindow.webContents.once("did-finish-load", () => {
if (darkMode) {
docsWindow.webContents.send("dark-mode:toggle");
}
if (!isDarwin) {
docsWindow.webContents.send("platform:not-darwin");
}
docsWindow.webContents.send("docs:jump", section);
docsWindow.show();
});
In the docs window, there is more HTML content to be renderered than in the editor window. As a result, the DOM takes longer to load and the "ready-to-show" event is emitted before it has finished. Using the "did-finish-load" event, ensures that the API functions are not called before the DOM has finished loading.

Related

Open a custom URL using window.open() in Firefox

In Firefox 88 it seems that opening a custom deep-linking URL with window.open(customURL, '_parent') reloads the current tab? Is there any solution to this problem? Should I use an <iframe> instead of window.open()? The behavior is different in Chrome however.
Required behaviour: window.open() opens the deeplinking app while the web app (written in Angular) continues to work in the same frame.
Recommend an Iframe
Some browsers are particularly weary of window.open as its abused quite heavily and thus blocked. However, an iframe is pretty normal and if using an app scheme and supported, has the side-effect of opening up the app.
Here's an approach you could try.
function checkIsAppSupported(appSchemeUrl, msThreshold) {
return new Promise((resolve) => {
let docBlurOccurred = false
const iframe = document.createElement('iframe', {
src: appSchemeUrl,
style: 'display:none'
})
document.appendChild(iframe)
function onDocBlur() {
docBlurOccurred = true
}
document.addEventListener('blur', onDocBlur)
// or try the newer
// document.addEventListener('visibilitychange', onDocBlur)
setTimeout(() => {
// cleanup
iframe.remove()
document.removeEventListener('blur', onDocBlur)
resolve(docBlurOccurred)
}, msThreshold); // checks if document focus changed within ms
});
}
async function tryAppSupported() {
const isAppSupported = await checkIsAppSupported('my-app://', 500);
if (isAppSupported) {
// do something
}
}
tryAppSupported()
Meaning if the main window becomes out of focus near the time of iframe loading, you could reasonably assume the actual app scheme was loaded and moved focus off the current window.
There are some npms that do this type of thing for example
https://www.npmjs.com/package/open-native-app and has great support for caring about what type of OS checks this behavior.
Happy coding.

How to do a silent refresh via implicit flow in Angular?

so I have the problem that our token won't refresh. More than that, our entire website is getting duplicated. Here's the background:
We have the following auth config (more or less):
export const authConfig: AuthConfig = {
issuer: '[censored]',
redirectUri: window.location.origin + '/',
silentRefreshRedirectUri: window.location.origin + '/assets/login-sources/silent-refresh.html',
tokenEndpoint: '[censored]',
loginUrl: '[same as tokenEndpoint]',
clientId: '[censored]',
scope: '[censored]',
clearHashAfterLogin: true,
oidc: true,
};
And we also have a loginService, which does roughly the following onInit:
this.oauthService.configure(authConfig);
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
// this.oauthService.setupAutomaticSilentRefresh(); - didn't work
this.oauthService.loadDiscoveryDocument([censored]).then((doc) => {
// Subscribe to expiration event to refresh token.
this.oauthService.events
.pipe(filter(element => element.type === 'token_expires'))
.subscribe(
(a) => {
console.log("Token is about to expire! Refreshing!");
this.oauthService.silentRefresh().then(result => console.log(result)).catch(error => console.error(error));
}
);
if (this.userid == null) {
this.oauthService.tryLoginImplicitFlow().then((loggedIn) => {
if (!loggedIn) {
this.oauthService.initLoginFlow();
}
});
}
});
The problem: whenever the token expired, the following things happen:
The entire website clones its instance so two instances are running in parallel (according to console)
=> this is even worse if we use automaticSilentRefresh without all those event listeners. There, it clones itself indefinitely until it runs out of RAM
A few seconds later we get a "refresh timeout" error in the console, without a stack trace.
So I looked into the code of silentRefresh() and it seems it works with an iframe. In said iframe SHOULD be just simple code that is used to refresh the token and "communicate to the main application" see here. We even implemented the refresh.html like here. In our case however, we have an iframe where the entire website is mirrored. This means we have something like this:
html
head /head
body
app-root /app-root
iframe
app-root /app-root
/iframe
/body
/html
Since for every silentRefresh(), the iframe gets removed and re-added into the DOM, each time a new instance of the whole website is created. What did I do wrong and how can I fix this?
Versions:
Angular 9 +
"angular-oauth2-oidc": "^9.0.1",
"angular-oauth2-oidc-jwks": "^9.0.0".
One way to deal with this, which I use for some samples of mine, is to only render the main app when running on the main window. In my small sample I use index.html for both the main app and the renewal iframe, but vary the code that runs.
Mine is a ReactJS Sample and this is the technique I have used. Hopefully this helps in a manner that isn't too intrusive, though there may be alternative solutions.
if (window.top === window.self) {
// Run the main ReactJS app
render (
<App />,
document.getElementById('root'),
);
} else {
// Run a minimal app to handle the iframe renewal
const app = new IFrameApp();
app.execute();
}

Is it possible to keep a subscription running even app is closed?

For background purposes I made a deep search in ionic documentation (native and no native), and in other pages, and found that may be there is a posibility to achieve this (keep running a subscription even the app was closed), but I can't understand at all where I should put one thing or other thing to make BakckgroundMode plugin working... (I think this plugin is not working in my device), but it's hard to inspect that thing because you can't inspect your device if it's in background mode with chrome://inspect...
This is the BackgroundMode plugin:
https://github.com/katzer/cordova-plugin-background-mode
I have installed backgroundMode 0.7.2 which isn't the most actualized plugin, but trying to install the latest gives me errors on build, so i tried this version and works.
In the docs, I see the following:
The plugin creates the object cordova.plugins.backgroundMode and is
accessible after the deviceready event has been fired.
So in order to run background mode, in my page.ts inside constructor (later I will talk about why in the constructor), I put:
document.addEventListener('deviceready', ()=> {
this.backgroundMode.enable();
this.backgroundMode.setDefaults(
{
title: 'executing in bg mode...',
text: 'background mode was activated successfully!',
color: '#fff',
resume: true,
silent: false
}
);
this.backgroundMode.disableWebViewOptimizations();
this.backgroundMode.on('activate').subscribe(()=>{
this.isAcceptedObservable = this.localNotifications.on('yes').subscribe(res =>{
console.log('confirmed!');
this.backgroundMode.unlock();
});
//And more observables...
});
});
the subscriptions inside backgroundMode.on('activate') subscription are the things that I need to keep running even if app is closed.
the reason of why I was put that code in constructor is because I'm using also ngx-socket-io to listen to emit and broadcast functions of a socket server in node that i created, so one of the subscriptions that is linked to socket events is:
this.getInvitationSubscriber = this.getInvitation().subscribe(data =>{
this.invitations.push(data);
this.showNotification({
title: 'Invitation received!',
text: 'Someone invited you!',
icon: data['from']['avatar_src'],
attachments: [data['from']['invitation_src']]
});
console.log(this.invitations);
});
in ionViewWillEnter i call:
ionViewWillEnter{
this.socket.connect();
}
and this function is outside the constructor:
getInvitation(){
let observable = new Observable(observer =>{
this.socket.on('subject', data =>{
observer.next(data);
});
});
return observable;
}
I'm expecting to keep running the 'notifications listener' that I have in constructor inside the backgroundMode.on('activate') function to receive notifications even app is closed, but I dont know if it's possible with ngx-socket-io and the structure that i have.
also I have injected background mode in app.module providers and in otherpage.module.ts provider and imported correctly so that's not the error, but I'm not getting notifications...
I know that notifications scheduling are correct because if i simple try not to use backgroundMode and putting that subscriptions outside backgroundMode.on('activate') subscription, i'm getting again notifications, but only works if app is in foreground or in suspended...

Cordova backbutton breaking application

I have a problem with Cordova's android application based on Angular 5+. I've found that window.history.back() and similar native JS back functions make two problems:
when going back, a page is flashing. It seems like first, all HTML content loaded and after it CSS
In one page on a back action, my layout is broken (screens below)
Orginal view:
After back button:
What's curious - after changing phone orientation all backs to normal.
I've found a solution - instead of using vanilla JS back functions I've created mine using Angular Router:
I've subscribe on router's events and save all routes:
this._subs.push(this._router.events.subscribe((e) => {
if (e instanceof NavigationEnd) {
this._cordovaService.saveRoute(e.url);
}
}));
And if I want back, I use navigateByUrl function:
back(): void {
const lastRoute = this._routingHistory[this._routingHistory.length - 2];
if (lastRoute) {
this._router.navigateByUrl(lastRoute);
this._routingHistory.pop();
}
}
After implementing this functionality for my inApp back buttons all work fine - there is no flashing or breaking layout.
Although, after implemented this function for my physical back button, the error is the same - layout breaking or flashing. Below my implementation:
Service:
this.deviceReady = Observable.fromEvent(document, 'deviceready').publishReplay(1);
(this.deviceReady as ConnectableObservable<Event>).connect();
this.restore = Observable.fromEvent(document, 'resume').publishReplay();
(this.restore as ConnectableObservable<Event>).connect();
this.backbutton = Observable.fromEvent(document, 'backbutton').publishReplay();
(this.backbutton as ConnectableObservable<Event>).connect();
Using it:
this._subs.push(this._cordovaService.deviceReady.subscribe(
() => {
document.addEventListener('backbutton', function (e) {
e.preventDefault();
e.stopPropagation();
this._cordovaService.back();
}.bind(this), false);
}
)
);
I'm sure that function in backbutton is executed (I've logged some information) but the problem still occurs.
More information:
I'm using cordova version 8.0.0
I'm using the following plugins:
https://github.com/TheMattRay/cordova-plugin-wkwebviewxhrfix.git" />
Some hints:
Once, I've built Cordova's android applications which work great (with native JS back function) but after next build, all come back. In hockeyapp I see that in good working version lowest available Android version was 4.1. In new apps, it is 4.4.
I've tried to downgrade Cordova/android engine version but without any positive results.
Additionally, I want to work with the newest libraries available.
Thanks for any help in that case.
I've finally found the solution, based on the following blog's post: http://weblogs.thinktecture.com/thomas/2017/02/cordova-vs-zonejs-or-why-is-angulars-document-event-listener-not-in-a-zone.html, I've added below script just before cordova.js import:
<script>
(function () {
'use strict';
window.addEventListener = function () {
EventTarget.prototype.addEventListener.apply(this, arguments);
};
window.removeEventListener = function () {
EventTarget.prototype.removeEventListener.apply(this, arguments);
};
document.addEventListener = function () {
EventTarget.prototype.addEventListener.apply(this, arguments);
};
document.removeEventListener = function () {
EventTarget.prototype.removeEventListener.apply(this, arguments);
};
})();
</script>
<script type="text/javascript" src="cordova.js"></script>
More about why this error occurring you can read in this GitHub issue: https://github.com/angular/angular/issues/22509

Electron does not listen keydown event

I am a backend developer who got a little project to fix it.
So my boss gives me an electron project which runs on touch devices.
I know that I can listen any key events in Javascript if I use the document object, but in electron it does not work, it says the docuemnt cannot be found.
So implemented this when I or other support guy press the F12 button then the dev tools be rendered out in the electron app.
mainWindow = new BrowserWindow({
'web-preferences': {'web-security': false}
});
mainWindow.onkeydown = function (e) {
console.log("Key down");
if (e.which === 123) {
console.log("Key is F12");
mainWindow.webContents.openDevTools();
}
};
But this code is not working to me. I have no idea how I can listen the F12 button is pressed.
Unfortunately I cannot render out button to the UI which can show the devtools. Because of the customers mustn't press it.
Sometimes I need to see the realtime console tab in devtools on the device.
There is a known issue in Electron ( which has lately been marked as wontfix ) that prevents the usual approach to catch key events using the traditional JS approach.
There also is a small library called electron-localshortcut that circumvents this issue by hijacking the Electron global shortcuts API when the window is active.
Use like this in your main.js:
const electronLocalshortcut = require('electron-localshortcut');
electronLocalshortcut.register(mainWindow, 'F12', () => {
// Open DevTools
});
Without additional libraries you can use "globalShortcut" of electron
const { app, BrowserWindow, globalShortcut } = require("electron");
globalShortcut.register("CmdOrCtrl+F12", () => {
mainWindow.isFocused() && mainWindow.webContents.toggleDevTools();
});
I think F12 is preserved so I use ctrl+f12 which is not far off
You can use the library mousetrap to add global short cut, because it can be installed through node and could bypass the problem of electron mentioned in the accepted answer.
A code example in the render process would be:
var Mousetrap = require('mousetrap');
Mousetrap.bind('4', function() { console.log('4'); });

Categories

Resources