Im trying to clear my session when the user leave my site.
when i try to do like below using 'beforeunload' event, even when i try to give enter on my site/application url also my handleBeforeUnload is getting called. Ideally i want to clear if the url(host/origin) is entered is different from my site then only i want to clear my session.
how to escape calling handleBeforeUnload - if the user enter my site specific urls ? or any other possible solution to achieve this request would be appreciated.
Below code i tried with my app.js
const getAppOrigin = window.location.origin;
useEffect(() => {
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [getAppOrigin]);
const handleBeforeUnload = ev => {
if (ev || window.event) {
// clear sessions code here
}
};
Related
I'm doing a multistep form and I want to prevent the user from leaving during the process. The idea is that if the users try to go away while doing the process, show them a confirmation message, if they confirm, then redirect them to the home page, else stay in the current page.
Currently I am using a < Prompt/> from React router
<Prompt when={isLeaving} message={"Changes will be lost"}/>
This displays a confirmation, so I would like to redirect the users to the home page if they accept.
I tried also with event listeners and I almost get it done. The only problem with this was that it only detects popstates.
useEffect(() => {
if(!popEventListnerAdded){
window.addEventListener('popstate', handleBackButton, false);
popEventListnerAdded = true;
}
}, []);
const handleBackButton = event => {
var r = window.confirm("You will need to begin the process again are you sure?");
if (r === true) {
// Call Back button programmatically as per user confirmation.
history.push("/Home");
} else {
// Stay on the current page.
history.push(location.pathname)
}
}
Is there any way to add a function when the user clicks OK on the < Prompt /> so I can redirect them?
Thanks a lot!
I've got a nice DeviceMotionEvent request all working for Safari (or other browsers that require the permission), something along these lines:
// on click of a button
DeviceMotionEvent.requestPermission()
.then(response => {
if (response == 'granted') {
// do my thing
}
})
.catch(function(error) {
console.log(error);
// do my other thing
});
And thats working great. But when a user goes to a new page, they have to request the permission again. Obviously I'm calling 'requestPermission' again, so of course they would do.
How do I find out if permission has already been granted? Or is permission granted on a page by page basis (rather than session / site basis)?
I could store the response in localstorage or something, but would prefer something along the lines of:
DeviceMotionEvent.permissionStatus
Or something?
I think you only option is to build a single page application and use the history.pushState() to update the location in the browser, when you wish to ‘change pages’.
Edited:
You can use the Web Storage API with the following two mechanisms:
sessionStorage
localStorage
As the names imply, these two interfaces are ideal for session-based data or if you want to persist state even after the connection is closed.
You should be able to check whether permissions have been granted using the devicemotion eventListener. Baring in mind you have to push a button or similar to run DeviceMotionEvent.requestPermission()
eg.
let hasOrientationControls = false;
window.addEventListener("devicemotion", () => {
hasOrientationControls = true;
});
// then when the button is pressed we can request permissions if we need
onButtonPressed () => {
if (hasOrientationControls) return;
else DeviceMotionEvent.requestPermission();
}
I've also used this
isVRReady = window.DeviceOrientationEvent && "ontouchstart" in window;
I have headless off and I want to wait untill user redirect to some page. I could use waitForRequest from puppeteer API but I don't know exact url it just must pass few circumstances.
So I use waitForFunction and check circumstances there but when I redirect to correct URL then I need to refresh page to pass circumstances for some reason.
My code is:
try {
await page.waitForFunction(() => {
if(window &&
window.location &&
window.location.hostname) {
const host = window.location.hostname.split('.');
if(!host.includes('www') &&
!host.includes('new') &&
host.includes('margonem') &&
host.length === 3) {
return true;
}
}
}, {
polling: 200,
timeout: 0
})
} catch (e) {
console.log(e);
}
and when I redirect to URL which pass all of above if's then I need to reload page to actually see that it return true. Why it works like this? I don't want user to be forced to refresh page after he enter correct one.
I have headless off and I want to wait untill user redirect to some page.
just use waitForNavigation().
In combination with a click make sure you use this pattern:
const [response] = await Promise.all([
page.waitForNavigation(waitOptions),
page.click(selector, clickOptions),
]);
waitForNavigation also returns a response object that you can then inspect
I could use waitForRequest from puppeteer API but I don't know exact url it just must pass few circumstances.
in this case puppeteer injects the request as argument and you can just test this in your lambda function. For example:
page.waitForRequest(request => {
return request.url().includes('margonem') && request.method() === 'GET'
})
The simplest way I have found is using waitForFunction, it is simple to modify to fit your specifications as well as compact.
await page.waitForFunction("window.location.pathname == '/Welcome.aspx'")
Sometimes we need to wait until we reach a specific URL.
The best way to handle it is
let holdProgress = true;
while (holdProgress) {
await page.waitFor(300);
if (page.url().includes('/env-selector')) {
holdProgress = false;
}
}
I have some pages on my site that allow users to create and edit posts. When a user is on these pages, I'd like to warn them before leaving the page. I can do that like this:
//Only warn if user is on a New or Edit page
if(window.location.href.indexOf("/new") !== -1 || window.location.href.indexOf("/edit") !== -1 {
window.addEventListener('beforeunload', function (e) {
e.preventDefault();
e.returnValue = '';
});
//Doing this again because I don't know which version is compataible with all browsers
window.onbeforeunload = function (e) {
e.preventDefault();
e.returnValue = ''
};
};
On a New or Edit page, the information in a form gets submitted to the server using JQuery ajax. The server returns a URL which the user gets redirected to to see the results of their post/update like this window.location.href = result; with result being the URL sent back from the server.
When that code runs to do the redirect, the user is getting the warning that they are about to leave the page they are on. I don't want it to do this on any redirects/navigation that the user has not performed. How could I stop/remove the warning in this instances?
UPDATE: This is not a duplicate. This question asks about preventing a beforeunloadevent happening on a redirect where the user has not requested to move away from the page himself.
Because you may want the event bound in some circumstances but not others within the same window, you'll have to not only add the event handler to the window, but you'll have to remove it as well (under the right circumstance) because even though you are changing the URL of the document loaded in the window, you are not changing the window itself:
function handleBeforeUnload (e) {
e.preventDefault();
e.returnValue = '';
}
//Only warn if user is on a New or Edit page
if(location.href.indexOf("/new") !== -1 || location.href.indexOf("/edit") !== -1 {
window.addEventListener('beforeunload', handleBeforeUnload);
} else {
// Remove the previously registered event handler (if any)
window.removeEventListener('beforeunload', handleBeforeUnload);
}
If you want to force a navigation with window.location.href, you should disable the beforeunload event listener before you navigate.
Something like this, for example:
function unloadHandler(e) {
e.preventDefault();
e.returnValue = '';
}
window.addEventListener('beforeunload', unloadHandler);
function forceNavigation(url) {
// Remove the "are you sure you want to leave?" message, then navigate
window.removeEventListener('beforeunload', unloadHandler);
window.location.href = url;
}
Call forceNavigation('https://example.com') to navigate without warning the user.
JS fiddle here: https://jsfiddle.net/o918wsam/1/
I'm trying to create an "Add To Home Screen" button on my progressive web app, as described in Chrome's documentation.
I'm generally following the prescribed pattern, where I have some hidden button which is displayed when Chrome's beforeinstallprompt event fires.
I capture the event once it fires, and then use the event to begin the native install dialogue once my own install button is clicked. The sample code is below:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
});
btnAdd.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
btnAdd.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
The issue I'm running into is that I don't want to show my install button (btnAdd) if the user has already installed the web app to thier home screen, and I'm having trouble figuring out how to check for that scenario.
I was hoping to modify the above code as follows:
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// If the user has not already installed...
deferredPrompt.userChoice
.then(choiceResult => {
if (choiceResult === undefined) {
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
}
});
});
So that the install button won't be displayed if the user has already installed. But this doesn't seem to work. It appears that if they haven't made a choice already, accessing userChoice just prompts the user directly with the native dialogue.
I'm not really sure how the beforeinstallevent works, so this might not even be a good strategy. Ideally I was hoping this would work something like something like navigator.serviceWorker.ready(), which returns a Promise rather than using browser events to try and figure out when stuff is ready.
In any case, are there any ideas on how I can check that the user has installed to home screen before I show my own home screen install button?
Edit: As Mathias has commented, checking for the event before showing the button should be sufficient. I believe the issue I was having is a result of using localhost, which appears to continually fire the beforeinstallprompt event even after installation, which is not the intended behavior. Hosting the code solved the issue.
Perhaps, don't show the button until you intercept the automatic pop-up?
or
In your code, check to see if the window is standalone
If it is, you need not show the button
if (window.matchMedia('(display-mode: standalone)').matches) {
// do things here
// set a variable to be used when calling something
// e.g. call Google Analytics to track standalone use
}
My example tester here
https://a2hs.glitch.me
Source code for my tester
https://github.com/ng-chicago/AddToHomeScreen
To answer original question. With latest versions of Chrome you can use window.navigator.getInstalledRelatedApps(). It returns a promise with an array of installed apps that your web app specifies as related in the manifest.json. To enable this to work you need to add related_applications field to manifest.json
"related_applications": [{
"platform": "webapp",
"url": "https://app.example.com/manifest.json"
}]
And then you can use it like:
//check if browser version supports the api
if ('getInstalledRelatedApps' in window.navigator) {
const relatedApps = await navigator.getInstalledRelatedApps();
relatedApps.forEach((app) => {
//if your PWA exists in the array it is installed
console.log(app.platform, app.url);
});
}
Source: API docs
Now you can display some elements depending if your app is installed. E.g: you can display "Open app" button and redirect user to PWA. But remember to disable it when the user is already in the app using #Mathias's answer and checking (display-mode: standalone)
However, regarding your use case. You should display install button only when beforeinstallprompt is intercepted. Browser does not fire this event if the PWA is already installed on the device. And when prompt is fired and choiceResult.outcome === 'accepted' you hide the button again.
I don't see how this is the correct answer, because this is basically a check if user uses the App already, but the behavior we wan't is "When the user is on the web and tries to install the app again to tell him that he already has the app in his device". Upon me this is not an answer that solves this.
What we can do is:
1. When the user clicks install but has the application on his device
In this case the beforeinstallprompt event WON'T BE fired so this event will return null. We store the result in global variable and when the result is null we show this to user that he already has the app installed.
2. When the user clicks install but doesn't have the application on his device
In this case the beforeinstallprompt event WILL be fired so this event will return access to show the prompt.
We can store the result in global variable and if it is not NULL (which won't be) because beforeinstallprompt will be fired if the user don't have the app on his device we show the prompt() to the user.
I doubt if mine solution is good too but I think that the Question and the correct answer don't have nothing in common
window.addEventListener("beforeinstallprompt", event => {
window.deferedPrompt = event;
});
handleButtonClick = () => {
const promptEvent = window.deferedPrompt;
if(!promptEvent){
// DO SOMETHING
}
//Show the add to home screen prompt
promptEvent.prompt()
promptEvent.userChoice.then((result: any) => {
// Reset the deferred prompt variable, since
// prompt() can only be called once.
window.deferedPrompt = null;.
});
}
<button onClick={handleButtonClick}>Install</button>
Here is the simple function that tells you, this app is open in browser or in pwa.
original source link
function getPWADisplayMode() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (document.referrer.startsWith('android-app://')) {
return 'twa';
} else if (navigator.standalone || isStandalone) {
return 'standalone';
}
return 'browser';
}
HTML
<!-- start with hidden button -->
<button id="install" style="display:none;">install</button>
JAVASCRIPT
// variable store event
window.deferredPrompt = {};
// get button with id
const install_button = document.querySelector('#install');
// if the app can be installed emit beforeinstallprompt
window.addEventListener('beforeinstallprompt', e => {
// this event does not fire if the application is already installed
// then your button still hidden ;)
// show button with display:block;
install_button.style.display = 'block';
// prevent default event
e.preventDefault();
// store install avaliable event
window.deferredPrompt = e;
// wait for click install button by user
install_button.addEventListener('click', e => {
window.deferredPrompt.prompt();
window.deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
// user accept the prompt
// lets hidden button
install_button.style.display = 'none';
} else {
console.log('User dismissed the prompt');
}
window.deferredPrompt = null;
});
});
});
// if are standalone android OR safari
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
// hidden the button
install_button.style.display = 'none';
}
// do action when finished install
window.addEventListener('appinstalled', e => {
console.log("success app install!");
});
Another way I've found to do this is to use IndexedDB. I import "idb-keyval" (https://www.npmjs.com/package/idb-keyval) to make it simple to get/set to the IndexedDb. Then I store a value that gets checked on the next page load. One difference with this method, is that it will let you check if your application is installed already if you visit the application webpage from the browser instead of the installed app shortcut.
import * as IDB from "idb-keyval";
let deferredPropt;
// Get the stored value from the IndexedDb
IDB.get("isInstalled").then((val) => {
if (val) {
// If it exists, run code based on an installed pwa
} else {
log({ isInstalled: false });
}
});
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault();
deferredPrompt = e;
document.getElementById("installApp").addEventListener("click", async () => {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
if (outcome == "accepted") {
// Set the value into the IndexedDb on installation of the PWA
IDB.set("isInstalled", true);
}
});
});