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

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...

Related

Are all these multiple checks really required to handle a new service worker update?

I am following a tutorial on service workers on Udacity by Jake Archibald, and this is the solution skeleton of an exercise on "updating" that has confused me:
They give a long solution that is like the code below (they check for three possible cases, as explained in comments):
(async() => {
if ("serviceWorker" in navigator) {
try {
const reg = await navigator.serviceWorker.register("/sw.js");
if (!reg || !navigator.serviceWorker.controller) {
return;
}
// Possible states of new updates:
// - 1. There are no updates yet, a new update may arrive
// - 2. An update is in progress
// - 3. A waiting update exists (already installed)
// 1.
addEventListener("updatefound", () => {
console.log("updatefound");
const sw = reg.installing;
trackInstallation(sw);
});
// 2
const installingSw = reg.installing;
if (installingSw) {
console.log("installingSw");
trackInstallation(installingSw);
return;
}
// 3
if (reg.waiting) {
console.log("reg.waiting");
const sw = reg.waiting;
notifyUpdate();
return;
}
console.log("nothing");
} catch (error) {
console.error("Service worker registration failed.", error);
}
}
})();
function trackInstallation(worker) {
worker.addEventListener("statechange", () => {
if (worker.state === "installed") {
notifyUpdate();
}
});
}
function notifyUpdate() {
alert("There's a new update!");
}
But I tried different scenarios and I can't get these checks except the third one (if (reg.waiting) {) to be triggered. So I wonder if all these checks are really needed?
This is how I trigger the third check:
Install the service worker (register, install, activate) by loading the web page for the first time at localhost:8080
Make a change to the service worker (e.g., add "/tmp.txt" to the array of names of the files that have to be cached)
Refresh the page.
👉 First load none of the checks are triggered.
👉 Second load, reg.waiting runs (the third check is triggered).
which makes sense (I've read and I know how the lifecycle of service workers works), but I don't know in what scenario the other two checks (i.e., the ones on line 14 and 21) would be triggered?
Things to remember:
The service worker isn't for a single page
The service worker runs independently of any page
In your example code, one page installs the service worker, and ensures that the install completes entirely.
That isn't always the case in the real world. Let's say:
You refresh a service-worker-controlled page.
That triggers a service worker update check. Assuming an update is found:
// 1 will happen for all pages in the origin. You might not be seeing it, because your code is addEventListener('updatefound', where it should be navigator.serviceWorker.addEventListener('updatefound'.
If at this point, you reload the page, you might hit // 2, since there's already an install in progress.
For more details on the service worker lifecycle, see https://web.dev/service-worker-lifecycle/

Node-global-key-listener package Linux

I tried to implement node-global-key-listener package for my project and was successful in Windows build as well as in Mac.When I tried it in Ubuntu, for some reason the listener is not listening. I am currently in v20.04.
Upon tracing the code, this listener on the x11ServerKey does not seem to get called.
this.proc.stdout.on("data", data => {
const events = this._getEventData(data);
for (let { event, eventId } of events) {
const stopPropagation = !!this.listener(event);
this.proc.stdin.write(`${stopPropagation ? "1" : "0"},${eventId}\n`);
}
});
The process child does get initialized. Just listener not listening. The cpp part is a little outside of my expertise.
Any idea how to go about this? Let me know if you need more info.
Thank you so much.
I have traced the code up to the part where x11 server key is getting executed as a node child process. I think it is getting initialized but it is not emitting the on 'data' that handles the event.

Pass a puppeteer based client object to onClick button inside a puppeteer page

I have a non-common use case I'd like to implement. I found an open source project that uses puppeteer to automate action within whatsapp web. It provides a client object with an API to listen to events or do some actions such as
client.on('message_create', (msg) => {
client.sendMessage(number, text);
});
I'm not so proficient with JS, but what I understand is that it launches a real instance of whatsapp web and then interacts with it.
What I want to do is - once whatsapp web is loaded, add a div with buttons that would interact with my wrapper app functions and do automated stuff.
I did manage to add external button to the UI and set it's onClick, I done it via the the page.evaluate function, something like that:
await client.puppeteerPage.evaluate(async () => {
let btn = document.querySelector('#updateIcButton');
btn.addEventListener("click", async ()=>{
// Do some action with the client, however - client is undefined here
});
});
So far so good, but now comes the problem - what I want to do is to interact with the client functions inside the click listener, which I currently found impossible to do since inside the 'evaluate' function which is the only way I found to inject buttons, the client from the upper scope is undefined... I couldn't find a way to pass the client object as it creates a cyclic dependency or something if I pass as parameter.. Also tried other stuff such as calling exposeFunction or addScriptTag but nothing seem to be working.
I'm not sure it's possible anyhow, as although I'm not so proficients with JS and web dev, I understand that the web page doesn't "know" or can't interact with the puppeteer application wrapping it.
I'd appreciate if someone can please suggest a way to solve or another possible way to add UI that can interact with my puppeteer application and do automated stuff within the page instance.
Appreciate your help!
More of my source code:
client.on('ready', async () => {
await client.pupPage.exposeFunction("sendMessage", client.sendMessage);
client.pupPage.evaluate((divHtml) => {
//here I have logic to load a div container with a single button
//Once the div is added, I'm querying the button ID and trying to set it's onClick to interact with the client from above
let btn = document.querySelector('#updateIcButton');
btn.addEventListener("click", async ()=>{
// Do some action with the client
sendMessage("phonenumber","sometext"); // trying to call the func I exposed
});
}, divHtml);
});

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();
}

How can I access the DOM of a <webview> in Electron?

I'm just getting started with Electron, with prior experience with node-webkit (nw.js).
In nw.js, I was able to create iframes and then access the DOM of said iframe in order to grab things like the title, favicon, &c. When I picked up Electron a few days ago to port my nw.js app to it, I saw advice to use webviews instead of iframes, simply because they were better. Now, the functionality I mentioned above was relatively easy to do in nw.js, but I don't know how to do it in Electron (and examples are slim to none). Can anyone help?
Also, I have back/forward buttons for my webview (and I intend on having more than one). I saw in the documentation that I could call functions for doing so on a webview, but nothing I have tried worked either (and, I haven't found examples of them being used in the wild).
I dunno who voted to close my question, but I'm glad it didn't go through. Other people have this question elsewhere online too. I also explained what I wanted to achieve, but w/e.
I ended up using ipc-message. The documentation could use more examples/explanations for the layperson, but hey, I figured it out. My code is here and here, but I will also post examples below should my code disappear for whatever reason.
This code is in aries.js, and this file is included in the main renderer page, which is index.html.
var ipc = require("ipc");
var webview = document.getElementsByClassName("tabs-pane active")[0];
webview.addEventListener("ipc-message", function (e) {
if (e.channel === "window-data") {
// console.log(e.args[0]);
$(".tab.active .tab-favicon").attr("src", e.args[0].favicon);
$(".tab.active .tab-title").html(e.args[0].title);
$("#url-bar").val(e.args[0].url);
$("#aries-titlebar h1").html("Aries | " + e.args[0].title);
}
// TODO
// Make this better...cancel out setTimeout?
var timer;
if (e.channel === "mouseover-href") {
// console.log(e.args[0]);
$(".linker").html(e.args[0]).stop().addClass("active");
clearTimeout(timer);
timer = setTimeout(function () {
$(".linker").stop().removeClass("active");
}, 1500);
}
});
This next bit of code is in browser.js, and this file gets injected into my <webview>.
var ipc = require("ipc");
document.addEventListener("mouseover", function (e) {
var hoveredEl = e.target;
if (hoveredEl.tagName !== "A") {
return;
}
ipc.sendToHost("mouseover-href", hoveredEl.href);
});
document.addEventListener("DOMContentLoaded", function () {
var data = {
"title": document.title,
"url": window.location.href,
// need to make my own version, can't rely on Google forever
// maybe have this URL fetcher hosted on hikar.io?
"favicon": "https://www.google.com/s2/favicons?domain=" + window.location.href
};
ipc.sendToHost("window-data", data);
});
I haven't found a reliable way to inject jQuery into <webview>s, and I probably shouldn't because the page I would be injecting might already have it (in case you're wondering why my main code is jQuery, but there's also regular JavaScript).
Besides guest to host IPC calls as NetOperatorWibby, it is also very useful to go from host to guest. The only way to do this at present is to use the <webview>.executeJavaScript(code, userGesture). This api is a bit crude but it works.
If you are working with a remote guest, like "extending" a third party web page, you can also utilize webview preload attribute which executes your custom script before any other scripts are run on the page. Just note that the preload api, for security reasons, will nuke any functions that are created in the root namespace of your custom JS file when your custom script finishes, however this custodial process will not nuke any objects you declare in the root. So if you want your custom functions to persist, bundle them into a singleton object and your custom APIs will persist after the page fully loads.
[update] Here is a simple example that I just finished writing: Electron-Webview-Host-to-Guest-RPC-Sample
This relates to previous answer (I am not allowed to comment): Important info regarding ipc module for users of Electron 1.x:
The ipc module was split into two separate modules:
ipcMain for the main process
ipcRenderer for the renderer process
So, the above examples need to be corrected, instead of
// Outdated - doesn't work in 1.x
var ipc = require("ipc");
use:
// In main process.
var ipcMain = require('electron').ipcMain
And:
// In renderer process.
var ipcRenderer = require('electron').ipcRenderer
See: http://electron.atom.io/blog/2015/11/17/electron-api-changes section on 'Splitting the ipc module'

Categories

Resources