I am using Electron do make a desktop version on my Ionic React app. I've decided that the splash screen is only going to disappear when the React app says it can.
In index.ts (the main Electron file), I create an event listener like this:
ev = splashScreen.on(splashScreen.events.APP_READY_TO_START, () => {
splashScreen.off(ev, true);
splashScreen.destroy();
mainWindow.show();
}, true);
This listens for the OL-APP_START_SPLASH_CLOSE event through ipcMain. The on function looks like this:
on(event: string, fn: () => any, isExternal?: boolean) {
this.activeEvents.push({
[isExternal ? "EXTERNAL_" + event : event]: fn
});
if (isExternal) ipcMain.on(event, fn);
}
Following the event listener, I call an update function which (temporarily) delays execution for 7 seconds. After that, I emit an event:
splashScreen.emit(splashScreen.events.UPDATE_CHECK_FINISHED, true);
where emit is:
emit(event: string, isExternal?: Boolean) {
this.activeEvents.forEach(e => {
if (!Object.keys(e)[0].startsWith("EXTERNAL_")) e[event].bind(this).call(); //If it's an external event (i.e. `ipcMain`) then don't run callback
});
if (isExternal) this.splashScreen.webContents.send(event);
}
On the React side, in componentDidMount I have:
ipc.on("OL-CHECKED_FOR_UPDATES", () => {
ipc.send("OL-APP_START_SPLASH_CLOSE");
});
This will signal the splash screen to close only when updates have finished being checked for. When I emit the OL-CHECKED_FOR_UPDATES event this event listener should be called however it isn't.
I am able to send events fine (ipc.send works on it's own) but no matter what I do, I can't get it to listen for events.
I know these are being fired because if I move the event listener into any other script (ie index.ts) the event listener will fire, so it is only there it won't fire.
What could be the reason for this?
Just needed to sleep on it. Of course, if I create two different windows, they're going to have different render processes so when I emit an event on one, I'm not going to be able to receive it on the other.
All I had to do was send the event from the main window (the one displaying the react app) instead of the splash screen.
Related
I'm using a realtime listener on a firestore collection to get the latest data. The function I'm using is like below,the function is attached to a button press.
function getItems(selectedCategory){
const subscriber = firestore()
.collection('CollectionName')
.doc('documentName')
.collection('CollectionName2')
.where("category", "==", selectedCategory)
.onSnapshot(documentSnapshot => {
console.log('Total Docs: ', documentSnapshot.size)
documentSnapshot.forEach(documentSnapshot => {
console.log('doc ID: ', documentSnapshot.id);
});
});
unSubscriptions.push(subscriber); //to detach the listener when page is changed(usefocuseffect)
}
when I call the function I can get the results and render them accordingly. But if I invoke the function again with a different input variable for "selectedCategory" it also logs the results. The thing is if any changes happened with the first occurrence (the first input variable) it also gets logged. I believe this happens because the listener for the first input parameter is still being active. Is there a way I can detach the first listener before invoking a second one.
P.S : I'm using a return function with useFocusEffect to cleanup the listeners when the page goes out of focus(which works) but I want to do a similar thing with a button press.
onSnapshot() returns a function that you need to call to detach your listener.
So you need to simply do subscriber() (note the parentheses) when clicking your button.
The specific doc is here.
Is there any way to pause firestore listener without removing it?
I have multiple firebase listeners, some are dependent on other, that changes or start other listeners on data change. Lets say my first listener starts a second listener its onSnapshot. First listener started on useEffect. For certain condition I may not want to change the second listener, so I need to discard data change update from first listener.
If condition met (button click), I discard data changes on first listener for a few moments. Currently I'm doing this using a boolean with useRef. My react app is working fine, with dependant listeners like this. I could remove the listener but I do not want to remove and recreate the listener.
I was wondering if there is a pausing mechanism or method available for any listener. I think it will save a tiny read cost if there was such a method because I'm not using that data sent onSnapshot.
Code example:
useEffect(() => {
let firstListener, secondListener;
//console.log("useEffect...");
function ListenerFunc(p) {
secondListener = await firestore
.collection("test")
.doc(p)
.onSnapshot((doc) => {
//console.log("Current data: ", doc.data());
//Need to discard unwanted change here.
//Changing it on button click for a 2 seconds then it changes back to : pauser.current = false.
if (pauser.current) {
console.log("paused for a moment.");
//pauser.current = false;
return;
}
else {
//update.
}
})
}
firstListener = firestore
.collection("test")
.doc("tab")
.onSnapshot((doc) => {
//console.log("Current data: ", doc.data());
var p = doc.data().p; //get variable p
ListenerFunc(p);
});
// cleanup.
}
Unfortunately this is not possible. If you need to stop listening for changes, even temporarily, you have to detach your listener and attach a new one when you want to start listening again, there is no pause mechanism for listeners.
You could open a Feature Request in Google's Issue Tracker if you'd like so that the product team can consider this, but given that this has already been proposed in this GitHub Feature Request for the IOS SDK and it was rejected I don't see this changing anytime soon.
I am making a basic web app with ReactJS to create push notifications.
I want to let the user the possibility to register manually the service worker by clicking on a button. But nothings happens (no message of success in the console). Did I make a mistake somewhere?
In a standard js file, the code to register the service worker works (without the button though) , but not in the react file.
the code to register the service worker :
and the event listener on the button :
Thanks in advance !
The main issue I see is that you attach an EventListener to window that would execute on 'load'. Since this event is most likely already triggered since you can click on a button, the SW is not getting loaded.
Try removing the eventhandler part to directly call the register function.
It would then look something like this:
function registerSW() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js')
//...
}
}
Another issue I see is that you try to set state by directly assigning it. In react you shouldn't access state like that but rather call this.setState like this:
// ...
.then(swReg => {
console.log('SW registered');
this.setState({ swReg: swReg });
})
// ...
I am using a library (https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu) that scrolls on use of the mousewheel, and I want to use this functionality when swiping left or right.
I am using hammerjs to replicate swipeleft and swiperight behavior, and this is working.
However, creating a WheelEvent does not seem to trigger the functionality dependent on the WheelEvent.
I am using componentDidUpdate for now as my react lifecycle method because for some reason this.containerRef.current is always null in componentDidMount, but once I figure out the reason behind that, I'll probably move it.
Anyway, here's my code:
componentDidUpdate() {
if(this.containerRef.current !== null) {
this.hammer = Hammer(this.containerRef.current)
this.hammer.on('swiperight', () => alert("swipe right"));
var wheelevent = new WheelEvent("wheel", {deltaX: 500, deltaY: 500});
this.hammer.on('swiperight', () => window.dispatchEvent(wheelevent));
}
}
Now I want to point out, the alert for swipe right DOES happen, so the behavior is definitely triggering, however my WheelEvent is not being caught by the scroll library.
How should I trigger a WheelEvent programmatically?
EDIT - I made a codepen about it:
https://codesandbox.io/s/react-horizontal-scrolling-menu-fi7tv
My hunch is that issue is related to Dragging being disabled and the event is canceled.
So you need to send the event down the chain a bit. I have updated the codesandbox below which works
https://codesandbox.io/s/react-horizontal-scrolling-menu-j46l8
The updated code part is below
var elem = document.getElementsByClassName("menu-wrapper")[0];
this.hammer.on("swiperight", () => elem.dispatchEvent(wheeleventRight));
this.hammer.on("swipeleft", () => elem.dispatchEvent(wheeleventLeft));
You may want to better the approach though in a more reactive fashion later. But this does show that once you sent the event in lower order elements the wheeling does work well
The justification
In my BB app, I allow rapid input from users which gets queued & sent off periodically in the background to the server. The problem I currently have is if a user leaves the page they effectively discard any pending changes sitting in the queue.
So basically what I want to do is inform the user before they leave to give them the opportunity to wait for the changes to be saved rather than just exiting & discarding.
The nitty gritty
So for the general cases where the user refreshes or attempts to navigate to an external URL we can handle the onbeforeunload event. Where it becomes slightly tricky is when we are in the context of an SPA whereby switching between pages does not cause a page refresh.
My immediate thought was to use a global click event handler for all anchors and validate whether or not I want to allow the click, which would work for in-site link navigation. However, where this falls over is navigating via the browsers Back/Forward buttons.
I also had a look at Backbone.routefilter, which at first glance appeared to do exactly what I needed. However, using the simple case as described in the docs, the route was still being executed.
The question
How do we intercept navigation for all scenarios within a Backbone SPA?
Direct link navigation
Use a global event handler to capture all click events
$(document).on('click', 'a[href^="/"]', function (e) {
var href = $(e.currentTarget).attr('href');
e.preventDefault();
if (doSomeValidation()) {
router.navigate(href, { trigger: true });
}
});
Page refreshing / external URL navigation
Handle the onbeforeunload event on the window
$(window).on('beforeunload', function (e) {
if (!doSomeValidation()) {
return 'Leaving now will may result in data loss';
}
});
Browser back/forward button navigation
Behind the scenes Backbone.Router uses the Backbone.history which ultimately leverages the HTML5 pushstate API. Depending on what options you pass to Backbone.history.start, and what your browser is capable of, the API will hook into either the onhashchange event or the onpopstate event.
Delving into the source for Backbone.history.start it becomes apparent that regardless of whether you are using push state or not, the same event handler is used i.e. checkUrl.
if (this._hasPushState) {
addEventListener('popstate', this.checkUrl, false);
} else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
addEventListener('hashchange', this.checkUrl, false);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
Therefore, we can override this method & perform our validation in there
var originalCheckUrl = Backbone.history.checkUrl;
Backbone.history.checkUrl = function (e) {
if (doSomeValidation()) {
return originalCheckUrl.call(this, e);
} else {
// re-push the current page into the history (at this stage it's been popped)
window.history.pushState({}, document.title, Backbone.history.fragment);
// cancel the original event
return false;
}
};