React native, get value from url browser - reactjs

How do i get the value of url from the browser? using the react native

If you can make callbacks from the gateway website, then I recommend to use deep linking to handle flow between app and browser. Basically, your app will open the gateway website for payment, and depending on payment result, the website will make a callback to the app using its deep link. App then will listen to the link, take out necessary information and continue to proceed.
What you need to do is:
Set up deep linking in your app. You should follow the guide from official website (here) to enable it. Let pick a random URL here for linking, e.g. gatewaylistener
Set the necessary callbacks from gateway to your app. In your case, since you need to handle successful payment and failed payment, you can add 2 callbacks, e.g. gatewaylistener://success?id={paymentId} and gatewaylistener://error?id={paymentId}
Finally, you need to listen to web browser from the app. One way to do that is add listener right inside the component opening the gateway.
// setup
componentDidMount() {
Linking.getInitialURL().then((url) => {
if (url) {
this.handleOpenURL(url)
}
}).catch(err => {})
Linking.addEventListener('url', this.handleOpenURL)
}
componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL)
}
// open your gateway
async openGateWay = () => {
const { addNewOrderGatewayToken } = this.props
const url = `${BASEURL}${addNewOrderGatewayToken}`
const canOpen = await Linking.canOpenURL(url)
if (canOpen) {
this.props.dispatch(setPaymentStatus('checked'))
Linking.openURL(url)
}
}
// handle gateway callbacks
handleOpenURL = (url) => {
if (isSucceedPayment(url)) { // your condition
// handle success payment
} else {
// handle failure
}
}

Related

Google One Tap SignIn Popup not showing

I was trying to implement Google One Tap SignIn in my project. At the first time after building the project the google one tap prompt will display. But next time onwards if we refresh the page also the prompt is not displaying.
Here is my code snippet.
import { addScript } from 'Util/DOM';
/**
* Loads One Tap Client Library
*/
const loadOneTapClientLibrary = async() => {
await addScript('https://accounts.google.com/gsi/client');
}
/**
* Loads One Tap Javascript API
* #param {*} resolve
*/
const loadOneTapJsAPI = (resolve) => {
window.onload = () => {
google.accounts.id.initialize({
client_id: "My client Id",
callback: data => resolve(data)
});
google.accounts.id.prompt();
}
}
export const loadOneTap = async() => {
return new Promise( (resolve, reject) => {
loadOneTapClientLibrary();
loadOneTapJsAPI(resolve);
})
}
After page loads i am calling loadOneTap();
To avoid One Tap UI prompting too frequently to end users, if users close the UI by the 'X' button, the One Tap will be disabled for a while. This is the so-called "Exponental Cool Down" feature. More detail at: https://developers.google.com/identity/one-tap/web/guides/features#exponential_cool_down
I believe you triggered this feature during development. To avoid this, use Chrome incognito mode (and restart the browser when necessary).
As noted by Guibin this is the OneTap exponential cool down feature, it can be easily triggered during development when testing auth flow, but also legitimately when the end user clicks the close icon by mistake. On sites where Google login is optional this might seem pragmatic (i.e. the user genuinely wants to dismiss the popup prompt in favor of alternative login methods), however on a site where Google is the sole login identity provider and you are using the Javascript API instead of HTML api then this can manifest as broken functionality - i.e. no login prompt - and you want to avoid telling your users to use incognito or clear cache/cookies at all costs..
You can potentially handle this with some fallback logic..
window.google.accounts.id.prompt((notification) => {
if(notification.isNotDisplayed() || !notification.isDisplayed()) {
// #ts-ignore
const buttonDiv = window.document.createElement("div")
buttonDiv.setAttribute("id", "googleLoginBtn")
document.getElementsByTagName('body')[0].appendChild(buttonDiv);
// #ts-ignore
window.google.accounts.id.renderButton(
document.getElementById("googleLoginBtn"),
{ theme: "outline", size: "large" } // customization attributes
);
}
This renders a button to login that isn't subject the one-tap cool down feature. We're early days into playing with this so there may be other invariants with regards to state you need to consider (e.g. can isNotDisplayed return true when already logged in) - we already observed some oddities where isDisplayed and isNotDisplayed can both be false on the same invocation of the callback.
Extra note: I recall reading the user can disable all one tap features too, so if you're using the javascript API instead HTML api you will need the fallback to SignIn with Google button.

Next JS - Handling getInitialProps on _app.js in SSR vs CSR

I am trying to create a Next JS application that handles the authentication and initial routing inside getInitialProps. I discovered this method can be executed either in the server or on the client.
My approach so far it's to have 2 different handlers based on detecting if I am in executing in the server checking for the presence of the req attribute inside of ctx.
This does the trick but doesn't feel like is the right way of doing. Can somebody, please, tell me if there is a cleaner way.
All authentication is handled in a separate subdomain, so I just need to redirect to the auth subdomain if there is no cookie or auth request fails for some other reason.
import "../../styles/globals.css";
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
MyApp.getInitialProps = async (appContext) => {
let cookie, user;
let ctx = appContext.ctx;
//Check if I am in the server.
if (ctx.req) {
cookie = ctx.req.headers.cookie
//Do auth request.
//Redirect base on user properties
// handle redirects using res object
ctx.res.writeHead(302, { Location: "/crear-cuenta"});
} else {
cookie = window.document.cookie;
//Do auth request.
//Redirect base on user properties
//Do redirects using client side methods (useRouter hook, location.replace)???
}
//Return pageProps to the page with the authenticted user information.
return { pageProps: { user: user } };
};
export default MyApp;
I think your code is clean enough. Of course you still can maintain it.
My suggestion would be as the followings:
MyApp.getInitialProps = async (appContext) => {
in this line you can use object destructuring technique to get the context straightforward:
MyApp.getInitialProps = async ({ ctx }) => {
then you won't need this line for example anymore : let ctx = appContext.ctx;
The most important part of your code which can be cleaned up by the way is the area that you have written your auth request twice in an if/else condition. I would suggest you to implement that part like this:
const cookie = ctx.req ? ctx.req.headers.cookie : window.document.cookie;
Although I would try to keep everything in getInitialProps on server side, In that case I make a small change to get the cookie as following and process it in server-side only.
const cookie = cookie.parse(ctx.req ? ctx.req.headers.cookie || "" : undefined);
Note that: I'm using a cookie parser which u can install the package yourself as well. (npm install cookie)
if you need to do an extra check on your cookie at client side, I will do that in componentdidmount or in case you are using react hooks in useEffect. But it is not necessary.
Now you can implement //Do auth request once, which will cause cleaner code and of course to reduce unnecessary repetition.

How to activate a react route and pass data from the service worker?

I have a SPA PWA React app.
It is installed and running in standalone mode on the mobile device (Android+Chrome).
Let's say the app lists people and then when you click on a person it diplays details using /person route.
Now, I'm sending push notifications from the server and receiving them in the service worker attached to the app. The notification is about a person and I want to open that person's details when the user clicks on the notification.
The question is:
how do I activate the /person route on my app from the service worker
and pass data (e.g. person id, or person object)
without reloading the app
From what I understand, from the service worker notificationclick event handler I can:
focus on the app (but how do I pass data and activate a route)
open an url (but /person is not a physical route, and either way - I want avoid refreshing the page)
You can listen for click event for the Notification which you show to the user. And in the handler, you can open the URL for the corresponding person which comes from your server with push event.
notification.onclick = function(event) {
event.preventDefault();
// suppose you have an url property in the data
if (event.notification.data.url) {
self.clients.openWindow(event.notification.data.url);
}
}
Check these links:
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event
https://developer.mozilla.org/en-US/docs/Web/API/Clients/openWindow
To answer my own question: I've used IndexedDB (can't use localStorage as it is synchronous) to communicate between SW and PWA, though I'm not too happy about it.
This is roughly how my service worker code looks (I'm using idb library):
self.addEventListener('notificationclick', function(event) {
const notif = event.notification;
notif.close();
if (notif.data) {
let db;
let p = idb.openDB('my-store', 1, {
upgrade(db) {
db.createObjectStore(OBJSTORENAME, {
keyPath: 'id'
});
}
}).then(function(idb) {
db = idb;
return db.clear(OBJSTORENAME);
}).then(function(rv) {
return db.put(OBJSTORENAME, notif.data);
}).then(function(res) {
clients.openWindow('/');
}).catch(function(err) {
console.log("Error spawning notif", err);
});
event.waitUntil(p);
}
});
and then, in the root of my react app ie in my AppNavBar component I always check if there is something to show:
componentWillMount() {
let self = this;
let db;
idb.openDB('my-store', 1)
.then(function (idb) {
db = idb;
return db.getAll(OBJSTORENAME);
}).then(function (items) {
if (items && items.length) {
axios.get(`/some-additional-info-optional/${items[0].id}`).then(res => {
if (res.data && res.data.success) {
self.props.history.push({
pathname: '/details',
state: {
selectedObject: res.data.data[0]
}
});
}
});
db.clear(OBJSTORENAME)
.then()
.catch(err => {
console.log("error clearing ", OBJSTORENAME);
});
}
}).catch(function (err) {
console.log("Error", err);
});
}
Have been toying with clients.openWindow('/?id=123'); and clients.openWindow('/#123'); but that was behaving strangely, sometimes the app would stall, so I reverted to the IndexedDB approach.
(clients.postMessage could also be the way to go though I'm not sure how to plug that into the react framework)
HTH someone else, and I'm still looking for a better solution.
I had a similar need in my project. Using your's postMessage tip, I was able to get an event on my component every time a user clicks on service worker notification, and then route the user to the desired path.
service-worker.js
self.addEventListener("notificationclick", async event => {
const notification = event.notification;
notification.close();
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(clientsArr => {
if (clientsArr[0]) {
clientsArr[0].focus();
clientsArr[0].postMessage({
type: "NOTIFICATION_CLICK",
ticketId: notification.tag,
});
}
})
);
});
On your react component, add a new listener:
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", message => {
if (message.data.type === "NOTIFICATION_CLICK") {
history.push(`/tickets/${message.data.ticketId}`);
}
});
}
}, [history]);

Create-react-app reload on service worker update

I want to modify create-react-app service worker file and implement popup message which will ask user to update app if newer service worker is ready to be activated. I'm almost done with the solution but have one pitfall. I want to reload the app when user confirms service worker update popup, so I've added some coded to the end of register function, see below:
export default function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location)
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return
}
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config)
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
"This web app is being served cache-first by a service " +
"worker."
)
})
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl, config)
}
let preventDevToolsReloadLoop
navigator.serviceWorker.addEventListener("controllerchange", function() {
// ensure refresh is called only once
if (preventDevToolsReloadLoop) {
return
}
preventDevToolsReloadLoop = true
console.log("reload")
window.location.reload(true)
})
})
}
}
But the problem is that it reloads the app also on first visit, when there doesn't exist any service worker yet. How can I solve it?
Update to react-scripts ^3.2.0. Verify that you have the new version of serviceWorker.ts or .js. The old one was called registerServiceWorker.ts and the register function did not accept a configuration object. Note that this solution only works well if you are Not lazy-loading.
then in index.tsx:
serviceWorker.register({
onUpdate: registration => {
alert('New version available! Ready to update?');
if (registration && registration.waiting) {
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
}
window.location.reload();
}
});
The latest version of the ServiceWorker.ts register()function accepts a config object with a callback function where we can handle upgrading. If we post a message SKIP_WAITING this tells the service worker to stop waiting and to go ahead and load the new content after the next refresh. In this example I am using a javascript alert to inform the user. Please replace this with a custom toast.
The reason this postMessage function works is because under the hood CRA is using workbox-webpack-plugin which includes a SKIP_WAITING listener.
More About Service Workers
good guide: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68
CRA issue discussing service worker cache: https://github.com/facebook/create-react-app/issues/5316
If you are not using CRA, you can use workbox directly: https://developers.google.com/web/tools/workbox
Completing #jfbloom22's answer:
As you probably want to ask the user after an update has been detected with something more complex than a plain alert, you need to ensure the registration object is available from inside the React's components tree and save it to use after the user accepts to update (for example, by clicking a button).
As an option, in a component you can create a custom event listener on a global object like document and fire this event when the onUpdate callback passed to serviceWorker.register(), passing to it the resgistration object as extra data.
This is exactly what my recently published Service Worker Updater does (some self-promotion). To use it you just need to:
Add it to the dependencies:
yarn add #3m1/service-worker-updater
Use it in your index.js:
import { onServiceWorkerUpdate } from '#3m1/service-worker-updater';
// ...
// There are some naming changes in newer Create React App versions
serviceWorkerRegistration.register({
onUpdate: onServiceWorkerUpdate
});
Use it in some of your React components:
import React from 'react';
import { withServiceWorkerUpdater } from '#3m1/service-worker-updater';
const Updater = (props) => {
const {newServiceWorkerDetected, onLoadNewServiceWorkerAccept} = props;
return newServiceWorkerDetected ? (
<>
New version detected.
<button onClick={ onLoadNewServiceWorkerAccept }>Update!</button>
</>
) : null; // If no update is available, render nothing
}
export default withServiceWorkerUpdater(Updater);
Try to add reload funtion in installingWorker.state === 'installed'
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// do something ..
}
}

Event listener for window.stripe

I have a Stripe integration in a React web front-end. My payment component attempts to run…
componentDidLoad() {
if ( window.hasOwnProperty('Stripe') ) {
this.setState({stripe: window.stripe(config.stripeKey)})
}
}
This sporadically fails as sometimes the React app will boot and get the component loaded before window.stripe exists. I’m currently resolving this with…
componentDidLoad() {
this.watchStripe = setInterval( () => {
if (!props.stripe && isBrowser && window.Stripe) {
this.setState({stripe: window.stripe(config.stripeKey)})
clearInterval(this.watchStripe)
}
}, 100)
}
Is there a more elegant solution to this, something that doesn't require an interval timer?
If you read the Advanced integrations section they suggest doing
componentDidMount() {
if (window.Stripe) {
this.setState({stripe: window.Stripe('pk_test_12345')});
} else {
document.querySelector('#stripe-js').addEventListener('load', () => {
// Create Stripe instance once Stripe.js loads
this.setState({stripe: window.Stripe('pk_test_12345')});
});
}
}
This adds an event listener for the load event on the script element that loads Stripe and sets the state when it is available (so no continuous polling with setInterval,setTimeout is needed)

Resources