How to get electron autoUpdater instance inside Angular 9 app? - javascript

So I have an application that can run in electron, and it can auto update. The update server is up and can be used.
The problem is, my updater process runs in electron main process, not inside Angular app. I want the users to be able to check, and manually update the application themselves, which I think we need to implement main process instance inside my Angular app.
Here is how I call the updater inside main.js.
if (!require('electron-is-dev')) {
require('./updater');
} else {
console.log('App is running in dev environment')
}
And here is a slice of my updater.js
const { app, dialog, autoUpdater } = require('electron');
const server = 'http://my-release-server.com:5000';
const url = `${server}/update/${process.platform}/${app.getVersion()}`;
console.log(`[autoUpdater] init ${url}`);
autoUpdater.setFeedURL({ url });
setInterval(() => {
autoUpdater.checkForUpdates();
}, 30000)
autoUpdater.on('checking-for-update', () => {
console.log('[autoUpdater] Checking for updates');
});
// And other events handler.
My autoUpdater works like charm when I am using default setup, which runs the updater inside the main electron process, but I can not show or inform the user about the updating process.
So, is there any solution or right way to implement this thing? I learnt in some article to use electron remote which handled inter process communication, can I use that? Can I use angular service to handle these things?
Thank you.

Related

How to call/use a variable in electron main process (index.js) to use in a react component

I am new in electron and currently developing a desktop app using electron with reactjs. I made a url protocol so i can open the app from browser while passing some parameters. I already figured out way to open the app from browser with passing some parameters. Is there any efficient approach to do this?
For example :
I store the protocol to a variable (urlParam).
index.js
app.setAsDefaultProtocolClient('openApp');
urlParam = process.argv[1];
mainWindow.webContents.openDevTools()
mainWindow.webContents.executeJavaScript(`console.log("${urlParam}")`)
The variable store a value like this openApp://id=12312412&pwd=12312313.
I need to pass and use urlParam to process the parameters in my react component.
For example I want to console.log the value when I click the button.
component.js (scratch)
export default function LintingTab(props) {
const getUrl= () => {
console.log('urlParam');
}
return (
<button onClick={getUrl}>Console log the url param</button>
)
}

why is Socket.io not working on client side?

I want to use socket.io-client on vanilla react without context.
This socket.on listener on third useEffect does not work if I don't re-render this page, where I have done setSocket(HomeScreen.jsx), the event no longer fires and I get Cannot read properties of null (reading 'emit'). So, if I want it to work, then I have to make this page re-render for the event to work after every successful event firing.
I am pretty sure this is a frontend mistake since I am console.logging all the socket.ids to emit successfully on the backend.
I would also like to know on how I can separate the third useEffect to a different component or have socket.io listeners on different component where I did not set the socket. Is it possible through passing components? If so, how?
I am new to socket.io-client and I want to use vanilla react without context. I thinik I am facing these issues due to not having proper structure on socket.io-client
This code for socket.io occurs after successful log in where I set the localStorage authToken and get to HomeScreen.jsx. Please help me out to better re-structure my socket.io code on the client side.
HomeScreen.jsx:
useEffect(() => {
if(localStorage.getItem('authToken') && !localStorage.getItem('showedLoginStatus')){
toast.success("Login successful!");
localStorage.setItem('showedLoginStatus', 'showed');
}
setSocket(io.connect(backendLink));
}, [])
useEffect(() => {
console.log(socket, localStorage.getItem('authToken'));
socket?.emit("newUser", localStorage.getItem('authToken'));
}, [socket])
useEffect(() => {
socket?.on("notifyFriendRequest", () => {
console.log('sent');
// readRequests();
})
return () => socket?.off("notifyFriendRequest");
}, [socket])

Service Worker determine number of clients

We've got a SaaS app that's PWA capable and we're using Workbox for the grunt work. Up to now we've been following the tried and trusted recipe of displaying an update is available banner to the users, prompting them to update their web app.
Viewing usage data (via Sentry.io) we've noticed that most users simply seem to ignore the update banner and continue with the version they're on.
So we're looking at trying something different. This is to perform the update automatically when they change the route in the web app (when we know there's an update available).
Testing this shows that it does work. However there's a side-effect, and that is if they've got web app open in multiple tabs, then all the tabs get updated. This could be problematic for users' if they've got an un-saved form open in one of the tabs in the background - they'll potentially loose their work.
This happens during this piece of code:
// app shell page, created lifecycle hook
document.addEventListener('swUpdated', this.SetPwaRegistration, { once: true })
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (this.refreshing) {
return
}
this.refreshing = true
window.location.reload()
})
// app shell page, method in methods collection
SetPwaRegistration (event) {
// call mutation to pass the registration object to Vuex
this.PWA_REGISTRATION_SET({ pwaRegistration: event.detail })
}
// main.js
router.afterEach((to, from) => {
// retrieve the registration object from Vuex
const pwaRegistration = app.$store.getters.pwaRegistration
if (pwaRegistration) {
pwaRegistration.waiting.postMessage('skipWaiting')
}
})
the above code is from our Vue.js app code-base, the this.refreshing is set to false by default in the data property collection.
What I'd like to know if whether it is possible to determine if the Service Worker has only one client under it's control (i.e. the web app is only open in 1 browser tab), and if this is the case, the auto-update can happen without potential issues. If there's more than one client, then we'll display the update banner as usual.
As a brief update to this, I've come across code examples similar to this:
self.clients.matchAll().then(clients => {
const clientCount = clients.length
// store count of client in Vuex
})
Which looks like an option (i.e. count how many clients there are, store this in Vuex store), I'm just not sure where it should be used.
If you want to orchestrate the reload from entirely within the service worker, you can effectively do that by using the WindowClient interface to programmatically navigate to whatever the current URL is, which is roughly equivalent to a reload.
The two things to keep in in mind that navigate() will only work on WindowClients (not Workers) and that you can only call it if the service worker controls the WindowClient.
Putting that together, here's something to try:
// This can be run, e.g., in a `message` handler in your SW:
self.clients.matchAll({
// These options are actually the defaults, but just
// to be explicit:
includeUncontrolled: false,
type: 'window',
}).then((clients) => {
if (clients.length === 1) {
clients[0].navigate(clients[0].url);
}
})

Use custom JavaScript code in a Vue.js app

I'm trying to insert JavaScript code in a Vue.js router app. I need to load data from the CMS the app is served from. In order to get the data from the CMS I have to use a JavaScript library from the CMS which is not made for Vue and is not exporting it's class/functions like modern JS. So I import the JS library from in the index.html by a script tag. This works as intended.
But now I have to use the class from this CMS JavaScript library.
Before writing this as a Vue-Router app I just have used Vue for templating purposes.
So I had some code packed in the window.onload event handler.
I have to create an instance for the CMS data access class.
But this leads to a build error (using vue-cli build). Since there
are no understandable error messages from the build process
I have to use trial and error. Even simple variable assignments like var a = 1 seem not to be allowed.
A console.log('something') works. But nothing else seemes to be allowed (except defining the onload-event handler)
I have added this code in a <script> Tag inside App.vue (which was created by vue-cli create)
window.onload = function() {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
UPDATE
After testing the different solutions from the answers I got aware that using non-instance variables seems to cause the build errors.
This gives an error:
waitForPlayerData = true;
This works:
this.waitForPlayerData = true;
I wouldn't recommend using window.load to run your code. There are more native approaches to do this in Vue.js.
What you should do in the case you want to run it in the main component of the app before it's been loaded is to put the code inside the beforeCreate lifecycle hook of the main component.
...
beforeCreate () {
this.cmsDataLoader()
},
methods: {
cmsDataLoader () {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
}
...
This will run the code everytime a component is created before the creation. You could also use the created lifecycle hook if you want to run it after the creation of the component.
Check the following link for more information about lifecycle hooks.
The best way to place JavaScript in Vue.js App is mounted function, it is called when the component is loaded:
export default {
name: "component_name",
mounted() {
let array = document.querySelectorAll('.list_item');
},
}
You don't need window.onload, you can just put whatever you want there. I'm not entirely certain when precisely in the lifecycle it renders and maybe someone can hop in and let us know but it for sure renders when page starts. (though it makes sense that it does before the lifecycle hooks even start and that it'll solve your needs)
Better & easier solution if you want to load it before Vue loads is to add it to the main.js file. You have full control there and you can load it before Vue initializes.
No need for window.onload there either, just put it before or import a JS file before you initialize Vue, because it's going to be initialized by order.

Auth0 client is null in Vue SPA on page refresh

I have a Vue SPA based on one of Auth0's quickstart apps (https://github.com/auth0-samples/auth0-vue-samples). Everything works fine out of the box, but as soon as I try using the Auth0 client in my component code I run into problems. I followed the "Calling an API" tutorial (https://auth0.com/docs/quickstart/spa/vuejs/02-calling-an-api), which unhelpfully only shows how to call an API using a button. What I want to do is trigger an authenticated call to my API on initial page load so that I can ensure certain data exists in my own API (or create it if it does not). This seems like it should be pretty straightforward. I just throw this code in my created hook of my Vue component:
await this.$auth.getTokenSilently().then((authToken) => {
// reach out to my API using authToken
});
This actually works fine if the app hot reloads from my npm dev server, it reaches out to my API, which authorizes the request using the token, and sends back the correct data. The problem is when I manually reload the page, which causes this:
Uncaught (in promise) TypeError: Cannot read property 'getTokenSilently' of null
at Vue.getTokenSilently (authWrapper.js?de49:65)
at _callee$ (App.vue?234e:49)
Inside the authWrapper.js file (where the Auth0 client lives), the function call is here:
getTokenSilently(o) {
return this.auth0Client.getTokenSilently(o);
}
When I debug the call, "auth0Client" doesn't exist, which is why it's failing. What I can't understand is the correct way to ensure it does exist before I attempt to make the call. There's nothing in the samples that indicates the right way to do this. I tried putting my component code in different components and different Vue lifecycle hooks (created, beforeMount, mounted, etc), all with the same result. The client becomes available after 800 ms or so, but not when this code executes.
This is clearly a timing problem, but it's not clear to me how to tell my component code to sit and wait until this.auth0Client is non-null without doing something horrible and hacky like a setInterval.
I figured out a workaround for now, which I'll add as an answer in case anyone else has this issue, although it's not really the answer I want. Per the authGuard, you can use the exported "instance" from the authWrapper and watch its "loading" flag before executing your code that depends on the auth0Client being ready, like this:
import { getInstance } from "./auth/authWrapper";
// ... Vue component:
created() {
this.init(this.doSomethingWithAuth0Client);
},
methods: {
init(fn) {
// have to do this nonsense to make sure auth0Client is ready
var instance = getInstance();
instance.$watch("loading", loading => {
if (loading === false) {
fn(instance);
}
});
},
async doSomethingWithAuth0Client(instance) {
await instance.getTokenSilently().then((authToken) => {
// do authorized API calls with auth0 authToken here
});
}
}
It's hardly ideal, but it does work.

Categories

Resources