Google analytics on react-admin - react-admin

What the best easiest way to use GA on react-admin app?
I known about npm module react-ga, but it tracks only homepage:
import ReactGA from 'react-ga';
ReactGA.initialize('UA-12345');
ReactGA.pageview(window.location.pathname + window.location.search);
Is any universal way to see page view on each resource page and each actions?

I was able to get this working by using a custom history.
import { createBrowserHistory } from 'history';
import ReactGA from 'react-ga';
// create the history
const history = createBrowserHistory();
// listen for a route change and record page view
history.listen(location => {
ReactGA.pageview(location.pathname);
});
// initialize GA
ReactGA.initialize('UA-000000000-1');
// record the first pageview because listen will not be called
ReactGA.pageview(window.location.pathname);
Docs for listen with the history package: https://github.com/ReactTraining/history/blob/master/docs/GettingStarted.md#listening
Do not forget to wire up your customer history in your Admin component:
<Admin
history={history}
>
...
</Admin>
See https://marmelab.com/admin-on-rest/Admin.html#history for more details

Related

Update all clients after a change in vuex state

I am using vue2 syntax and vuex , versions : vue/cli 4.5.13 and vue#2.6.14 and vuex 3.6.2
I have a simple to do project , for adding todos in a list, based on the 2019 vue tutorial by traversy.
I have a simple form ij my component to add a to do
<form #submit.prevent="onSubmit" >
and in my vuex store I have
const state = {
todos:''
};
const getters = {
allTodos: (state) => {return state.todos}
};
const actions = {
async addTodo({commit}, title){
const res = await axios.post('https://jsonplaceholder.typicode.com/todos', {
title,
completed:false
});
commit('newTodo', res.data);
}
};
const mutations = {
newTodo:(state, todo)=>(
state.todos.unshift(todo)
)
};
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
Thank you
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
No, it is not possible.
There is no link between all your clients. All your Vue/VueX code lives in a single client. Here's what you need to do to get where you want to go, and its a long way from here:
Build a backend server. Here's a Node.js guide
Build an APi in your server. Your clients will make requests to this server to get all todos, and post new todos to the server. Here's an express.js guide
You need a database to store your todos in the server. You can use something like MongoDB or an ORM like Sequelize for node.js.
Now you can either write a code to periodically request the server for todos in the background and update it in your vue components, or you can use a pub/sub library like pusher. Pusher uses WebSockets under the hood for maintaining a persistent bidirectional connection. If you want to, you can implement this on your own, you can read about it here, thanks to #Aurora for the link to the tutorial.
Here's a consolidated guide for doing all this:
https://pusher.com/tutorials/realtime-app-vuejs/
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
There's a couple of errors in your code:
change todos:'' to todos:[]
change state.todos.unshift(todo) to state.todos.push(todo)
This way, every time that you call addTodo action, all components connected to allTodos getter will show the latest todos
NOTE:
Vuex/Vue are reactive. So in every page that you see using that showcomponent will show you the last update. If you want to show in every USER CONNECTED, of course you don't need http request, you need WEBSOCKETS

How to create and use custom APIs in Spartacus storefront?

We have a custom API created in hybris and I need to use the data returned by that custom API in some Spartacus pages. I want to do this post login and want to call that api whenever the page refreshes.
Also I want to maintain the data in a state so that I can use it across the pages.
I know how to do it in Angular but I am confused how to do it in Spartacus. Can someone please help me
You should treat spartacus as external lib. If you know how implement it in Angular, just do it. Most of our actions like login are exported in public API:
import { ActivatedRouterStateSnapshot, AuthActions } from '#spartacus/core';
import { RouterNavigatedAction, ROUTER_NAVIGATED } from '#ngrx/router-store';
#Injectable()
export class YourEffects {
#Effect()
yourActionOnLogin$: Observable<YourActions.XXX> = this.actions$.pipe(
ofType(AuthActions.LOGIN),
map(() => new CheckoutActions.XXX())
);
#Effect()
yourActionOnNavigation$: Observable<YourActions.YYY> = this.actions$.pipe(
ofType<RouterNavigatedAction<ActivatedRouterStateSnapshot>>(
ROUTER_NAVIGATED
),
map(() => new YourActions.YYY())
);
}
You can create and provide own modules for part of ngrx store and occ adapters (API) as well.

How to setup sendSignInLinkToEmail() from Firebase in react-native?

Working on a react-native project using #react-native-firebase/app v6 we recently integrated signing in with a "magic link" using auth.sendSignInLinkToEmail
We couldn't find a good example on how to setup everything in react-native and had different problems like
- auth/invalid-dynamic-link-domain - The provided dynamic link domain is not configured or authorized for the current project.
- auth/unauthorized-continue-uri - Domain not whitelisted by project
Searching for information and implementing the "magic link login" I've prepared a guide on how to have this setup in react-native
Firebase project configuration
Open the Firebase console
Prepare firebase instance (Email Link sign-in)
open the Auth section.
On the Sign in method tab, enable the Email/Password provider. Note that email/password sign-in must be enabled to use email link sign-in.
In the same section, enable Email link (passwordless sign-in) sign-in method.
On the Authorized domains tab (just bellow)
Add any domains that will be used
For example the domain for the url from ActionCodeSettings needs to be included here
Configuring Firebase Dynamic Links
For IOS - you need to have an ios app configured - Add an app or specify the following throughout the firebase console
Bundle ID
App Store ID
Apple Developer Team ID
For Android - you just need to have an Android app configured with a package name
Enable Firebase Dynamic Links - open the Dynamic Links section
“Firebase Auth uses Firebase Dynamic Links when sending a link that is meant to be opened in a mobile application.
In order to use this feature, Dynamic Links need to be configured in the Firebase Console.”
(ios only) You can verify that your Firebase project is properly configured to use Dynamic Links in your iOS app by opening
the following URL: https://your_dynamic_links_domain/apple-app-site-association
It should show something like:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "AP_ID123.com.example.app",
"paths": [
"NOT /_/", "/"
]
}
]
}
}
IOS Xcode project configuration for universal links
Open the Xcode project and go to the Info tab create a new URL type to be used for Dynamic Links.
Enter a unique value in Identifier field and set the URL scheme field to be your bundle identifier, which is the default URL scheme used by Dynamic Links.
In the Capabilities tab, enable Associated Domains and add the following to the Associated Domains list: applinks:your_dynamic_links_domain
(!) This should be only the domain - no https:// prefix
Android
Android doesn’t need additional configuration for default or custom domains.
Packages
A working react-native project setup with react-native-firebase is required, this is thoroughly covered in the library own documentation, here are the specific packages we used
Note: using the dynamicLinks package can be replaced with react-native's own Linking module and the code would be almost identical
Exact packages used:
"#react-native-firebase/app": "^6.7.1",
"#react-native-firebase/auth": "^6.7.1",
"#react-native-firebase/dynamic-links": "^6.7.1",
Sending the link to the user email
The module provides a sendSignInLinkToEmail method which accepts an email and action code configuration.
Firebase sends an email with a magic link to the provided email. Following the link has different behavior depending on the action code configuration.
The example below demonstrates how you could setup such a flow within your own application:
EmailLinkSignIn.jsx
import React, { useState } from 'react';
import { Alert, AsyncStorage, Button, TextInput, View } from 'react-native';
import auth from '#react-native-firebase/auth';
const EmailLinkSignIn = () => {
const [email, setEmail] = useState('');
return (
<View>
<TextInput value={email} onChangeText={text => setEmail(text)} />
<Button title="Send login link" onPress={() => sendSignInLink(email)} />
</View>
);
};
const BUNDLE_ID = 'com.example.ios';
const sendSignInLink = async (email) => {
const actionCodeSettings = {
handleCodeInApp: true,
// URL must be whitelisted in the Firebase Console.
url: 'https://www.example.com/magic-link',
iOS: {
bundleId: BUNDLE_ID,
},
android: {
packageName: BUNDLE_ID,
installApp: true,
minimumVersion: '12',
},
};
// Save the email for latter usage
await AsyncStorage.setItem('emailForSignIn', email);
await auth().sendSignInLinkToEmail(email, actionCodeSettings);
Alert.alert(`Login link sent to ${email}`);
/* You can also show a prompt to open the user's mailbox using 'react-native-email-link'
* await openInbox({ title: `Login link sent to ${email}`, message: 'Open my mailbox' }); */
};
export default EmailLinkSignIn;
We're setting handleCodeInApp to true since we want the link from the email to open our app and be handled there. How to configure and handle this is described in the next section.
The url parameter in this case is a fallback in case the link is opened from a desktop or another device that does not
have the app installed - they will be redirected to the provided url and it is a required parameter. It's also required to
have that url's domain whitelisted from Firebase console - Authentication -> Sign in method
You can find more details on the supported options here: ActionCodeSettings
Handling the link inside the app
Native projects needs to be configured so that the app can be launched by an universal link as described
above
You can use the built in Linking API from react-native or the dynamicLinks #react-native-firebase/dynamic-links to intercept and handle the link inside your app
EmailLinkHandler.jsx
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, AsyncStorage, StyleSheet, Text, View } from 'react-native';
import auth from '#react-native-firebase/auth';
import dynamicLinks from '#react-native-firebase/dynamic-links';
const EmailLinkHandler = () => {
const { loading, error } = useEmailLinkEffect();
// Show an overlay with a loading indicator while the email link is processed
if (loading || error) {
return (
<View style={styles.container}>
{Boolean(error) && <Text>{error.message}</Text>}
{loading && <ActivityIndicator />}
</View>
);
}
// Hide otherwise. Or show some content if you are using this as a separate screen
return null;
};
const useEmailLinkEffect = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const handleDynamicLink = async (link) => {
// Check and handle if the link is a email login link
if (auth().isSignInWithEmailLink(link.url)) {
setLoading(true);
try {
// use the email we saved earlier
const email = await AsyncStorage.getItem('emailForSignIn');
await auth().signInWithEmailLink(email, link.url);
/* You can now navigate to your initial authenticated screen
You can also parse the `link.url` and use the `continueurl` param to go to another screen
The `continueurl` would be the `url` passed to the action code settings */
}
catch (e) {
setError(e);
}
finally {
setLoading(false);
}
}
};
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
/* When the app is not running and is launched by a magic link the `onLink`
method won't fire, we can handle the app being launched by a magic link like this */
dynamicLinks().getInitialLink()
.then(link => link && handleDynamicLink(link));
// When the component is unmounted, remove the listener
return () => unsubscribe();
}, []);
return { error, loading };
};
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(250,250,250,0.33)',
justifyContent: 'center',
alignItems: 'center',
},
});
const App = () => (
<View>
<EmailLinkHandler />
<AppScreens />
</View>
);
You can use the component in the root of your app as in this example
Or you can use it as a separate screen/route - in that case the user should be redirected to it after
the sendSignInLinkToEmail action
Upon successful sign-in, any onAuthStateChanged listeners will trigger with the new authentication state of the user. The result from the signInWithEmailLink can also be used to retrieve information about the user that signed in
Testing the email login link in the simulator
Have the app installed on the running simulator
Go through the flow that will send the magic link to the email
Go to your inbox and copy the link address
Open a terminal and paste the following code
xcrun simctl openurl booted {paste_the_link_here}
This will start the app if it’s not running
It will trigger the onLink hook (if you have a listener for it like above)
References
Deep Linking In React Native Using Firebase Dynamic Links
React Native Firebase - Dynamic Links
React Native Firebase - auth - signInWithEmailLink
firebase.google.com - Passing State In Email Actions
firebase.google.com - Authenticate with Firebase Using Email Link in JavaScript

Can you disable the React-Admin Undo feature in config?

The Undo feature is a great, but it can cause inefficiencies during development cycles.
Is there an easy way for us to disable it in our staging environment, or at least lower the timeout?
The Edit and Create components support the undoable parameter. So you could do like this <Edit {...props} undoable={false} > to disable the undo function for a specific Form
When I unterstand the documentation and src correctly, you have to override the notification component in order to change the autoHideDuration.
This is the time the notification is visible to the user and after the delay, the request is send to the api.
When you set it to 0 the requests should be send nearly immediately.
From the documentation - Theming - Notifications:
You can override the notification component, for instance to change
the notification duration. It defaults to 4000, i.e. 4 seconds, and
you can override it using the autoHideDuration prop. For instance, to
create a custom Notification component with a 5 seconds default:
// in src/MyNotification.js
import { Notification } from 'react-admin';
const MyNotification = props => <Notification {...props}autoHideDuration={5000} />;
export default MyNotification;
undoable can only be set on Edit component and not on the Create component.
Handle the formProps coming from the Create page by adding a custom variable to check if the props are indeed from the 'Create' page or from the server.
To customize notification for Create or Edit page you can pass the successMessage prop to the components
successMessage="Item created successfully" //or use translate
More about 'successMessage' can be found here - React Documentation
With react-admin v.4 the way to disable Undo feature so changes are immediately saved is to add mutationMode="optimistic" to Edit/Create component.
To change the success notification message use mutationOptions={{ onSuccess }} together with notify hook
import { useNotify, useRefresh, useRedirect, Edit } from 'react-admin';
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();
const onSuccess = () => {
notify(`Changes saved`);
redirect('/your-collection');
refresh();
};
return (
<Edit mutationMode="optimistic" mutationOptions={{ onSuccess }}>
...

Shopify - Get shop domain inside a app

I'm new to Shopify app developing and I'm using Node,Express for the back-end and react with polaris libaray.
My question is how to get the shop's domain the request is initiating throug h the app. When I searched I could only found one used in Ruby ShopifyAPI::Shop.current and I'm looking for the similar thing to use in node?
For examples check out https://github.com/BKnights/kotn-shopify-utils
Yes it uses a session.
The code is pretty idiosyncratic. I published it mostly as an easy way to share between my own projects but it's worked pretty well for those.
If you use this where you may scale your servers you'd need to replace the session engine with something more distributed. Cookie sessions work.
This expects to route the setup page of the app to /preferences. Look at that route with the validSession, session, middleware
For passing the domain to Polaris I put the shop info into a plain JS object on the containing page (it's a dustjs template):
<script type="text/javascript">
var KotN = {
shop:'{shop}',
apiKey: '{apiKey}',
shopOrigin: 'https://{shop}.myshopify.com',
locale:'{locale}' || (navigator.languages ? (navigator.language || navigator.languages[0]) : (navigator.userLanguage || navigator.browerLanguage))
};
</script>
and then the Polaris component looks like:
import * as React from 'react';
import {EmbeddedApp} from '#shopify/polaris/embedded';
import ShopProvider from './stores/ShopProvider';
import Status from './views/status';
const shop = KotN.shop;
const shopOrigin = KotN.shopOrigin;
const apiKey = KotN.apiKey;
console.log('shop: '+ shop +' and origin: '+ shopOrigin);
export default class MyApp extends React.Component {
render() {
return (
<EmbeddedApp
apiKey={apiKey}
shopOrigin={shopOrigin}
forceRedirect={true}
debug={true}
>
<ShopProvider>
<Status />
</ShopProvider>
</EmbeddedApp>
);
}
}
I know this is a old thread but i've stumbled here while i was searching for the answer and just wanted to share my solution with you guys that also have the same problem. This is how i'm getting shop domain.
let shopDomain = new URL(window.location).searchParams.get("shop");