Skip to content

Commit

Permalink
Merge pull request #664 from hackforla/google-analytics
Browse files Browse the repository at this point in the history
Add Google Analytics support
  • Loading branch information
entrotech authored Nov 29, 2020
2 parents 0163964 + 1625e14 commit ccd5300
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 124 deletions.
210 changes: 88 additions & 122 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Redirect } from "react-router-dom";
import React from "react";
import PropTypes from "prop-types";
import { Route, Redirect } from "react-router-dom";
import { useTracking } from "./hooks/useTracking";
import { createUseStyles } from "react-jss";
import { withToastProvider } from "./contexts/Toast";
import { UserContext } from "./components/user-context";
import TdmCalculationContainer from "./components/TdmCalculationContainer";
import ProjectsPage from "./components/Projects/ProjectsPage";
import Header from "./components/Header";
Expand All @@ -21,7 +22,6 @@ import ResetPassword from "./components/Authorization/ResetPassword";
import ResetPasswordRequest from "./components/Authorization/ResetPasswordRequest";
import "./styles/App.scss";
import PublicComment from "./components/PublicComment/PublicCommentPage";
import NavConfirmModal from "./components/NavConfirmModal";

const useStyles = createUseStyles({
root: {
Expand All @@ -31,131 +31,97 @@ const useStyles = createUseStyles({
}
});

const App = () => {
const App = ({ account, setLoggedInAccount, hasConfirmedTransition }) => {
const classes = useStyles();
const [account, setAccount] = useState({});
const [confirmTransition, setConfirmTransition] = useState(null);
const [hasConfirmedTransition, setHasConfirmedTransition] = useState(true);
const [isOpenNavConfirmModal, setIsOpenNavConfirmModal] = useState(false);

const setLoggedInAccount = loggedInUser => {
setAccount(loggedInUser);
localStorage.setItem("currentUser", JSON.stringify(loggedInUser));
};

useEffect(() => {
const currentUser = localStorage.getItem("currentUser");
if (currentUser) {
try {
const parsedAccount = JSON.parse(currentUser);
setAccount(parsedAccount);
} catch (err) {
// TODO: replace with production error logging.
console.error(
"Unable to parse current user from local storage.",
currentUser
);
}
} else {
setLoggedInAccount({});
}
}, []);

const getUserConfirmation = (_message, defaultConfirmCallback) => {
setHasConfirmedTransition(false);
setConfirmTransition(() => ({
defaultConfirmCallback: defaultConfirmCallback,
setHasConfirmedTransition: setHasConfirmedTransition
}));
setIsOpenNavConfirmModal(!isOpenNavConfirmModal);
};
useTracking("G-MW23VES3G6");

return (
<React.Fragment>
<UserContext.Provider value={account}>
<Router getUserConfirmation={getUserConfirmation}>
<NavConfirmModal
confirmTransition={confirmTransition}
isOpenNavConfirmModal={isOpenNavConfirmModal}
setIsOpenNavConfirmModal={setIsOpenNavConfirmModal}
/>
<Header account={account} />
<div className={classes.root}>
<Route
exact
path="/"
render={() =>
account.email ? (
<Redirect to="/create-project" />
) : (
<Login setLoggedInAccount={setLoggedInAccount} />
)
}
/>
<Route exact path="/create-project">
<Redirect to="/calculation" />
</Route>
<Route
path="/calculation/:page?/:projectId?"
render={() => (
<TdmCalculationContainer
account={account}
hasConfirmedNavTransition={hasConfirmedTransition}
setLoggedInAccount={setLoggedInAccount}
/>
)}
/>
<Route
path="/projects"
render={() => <ProjectsPage account={account} />}
<Header account={account} />
<div className={classes.root}>
<Route
exact
path="/"
render={() =>
account.email ? (
<Redirect to="/create-project" />
) : (
<Login setLoggedInAccount={setLoggedInAccount} />
)
}
/>
<Route exact path="/create-project">
<Redirect to="/calculation" />
</Route>
<Route
path="/calculation/:page?/:projectId?"
render={() => (
<TdmCalculationContainer
account={account}
hasConfirmedNavTransition={hasConfirmedTransition}
setLoggedInAccount={setLoggedInAccount}
/>
<Route path="/about" component={About} />
<Route path="/termsandconditions" component={TermsAndConditions} />
<Route path="/privacypolicy" component={PrivacyPolicy} />
<Route path="/register/:email?" component={Register} />
<Route path="/confirm/:token" component={ConfirmEmail} />
<Route
path="/login/:email?"
render={() =>
account.email ? (
<Redirect to="/create-project" />
) : (
<Login setLoggedInAccount={setLoggedInAccount} />
)
}
/>
<Route
path="/logout/:email?"
render={routeProps => {
setLoggedInAccount({});
return (
<Redirect
to={
routeProps.match.params["email"]
? `/login/${routeProps.match.params["email"]}`
: "/login"
}
/>
);
}}
/>
<Route path="/forgotpassword" component={ResetPasswordRequest} />
<Route path="/resetPassword/:token" component={ResetPassword} />
<Route path="/contactus" component={ContactUs} />
{account && account.isAdmin ? (
<Route path="/admin" render={() => <Admin account={account} />} />
) : null}
{account && account.isSecurityAdmin ? (
<Route path="/roles" render={() => <Roles />} />
) : null}
<Route path="/faqs" component={FaqView} />
<Route path="/publiccomment" component={PublicComment} />
</div>
<Footer />
</Router>
</UserContext.Provider>
)}
/>
<Route
path="/projects"
render={() => <ProjectsPage account={account} />}
/>
<Route path="/about" component={About} />
<Route path="/termsandconditions" component={TermsAndConditions} />
<Route path="/privacypolicy" component={PrivacyPolicy} />
<Route path="/register/:email?" component={Register} />
<Route path="/confirm/:token" component={ConfirmEmail} />
<Route
path="/login/:email?"
render={() =>
account.email ? (
<Redirect to="/create-project" />
) : (
<Login setLoggedInAccount={setLoggedInAccount} />
)
}
/>
<Route
path="/logout/:email?"
render={routeProps => {
setLoggedInAccount({});
return (
<Redirect
to={
routeProps.match.params["email"]
? `/login/${routeProps.match.params["email"]}`
: "/login"
}
/>
);
}}
/>
<Route path="/forgotpassword" component={ResetPasswordRequest} />
<Route path="/resetPassword/:token" component={ResetPassword} />
<Route path="/contactus" component={ContactUs} />
{account && account.isAdmin ? (
<Route path="/admin" render={() => <Admin account={account} />} />
) : null}
{account && account.isSecurityAdmin ? (
<Route path="/roles" render={() => <Roles />} />
) : null}
<Route path="/faqs" component={FaqView} />
<Route path="/publiccomment" component={PublicComment} />
</div>
<Footer />
</React.Fragment>
);
};

App.propTypes = {
account: PropTypes.shape({
email: PropTypes.string,
isAdmin: PropTypes.bool,
isSecurityAdmin: PropTypes.bool
}),
setLoggedInAccount: PropTypes.func,
hasConfirmedTransition: PropTypes.func
};

export default withToastProvider(App);
65 changes: 65 additions & 0 deletions client/src/AppWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { UserContext } from "./components/user-context";
import App from "./App";
import NavConfirmModal from "./components/NavConfirmModal";

const AppWrapper = () => {
const [account, setAccount] = useState({});
const [confirmTransition, setConfirmTransition] = useState(null);
const [hasConfirmedTransition, setHasConfirmedTransition] = useState(true);
const [isOpenNavConfirmModal, setIsOpenNavConfirmModal] = useState(false);

const setLoggedInAccount = loggedInUser => {
setAccount(loggedInUser);
localStorage.setItem("currentUser", JSON.stringify(loggedInUser));
};

useEffect(() => {
const currentUser = localStorage.getItem("currentUser");
if (currentUser) {
try {
const parsedAccount = JSON.parse(currentUser);
setAccount(parsedAccount);
} catch (err) {
// TODO: replace with production error logging.
console.error(
"Unable to parse current user from local storage.",
currentUser
);
}
} else {
setLoggedInAccount({});
}
}, []);

const getUserConfirmation = (_message, defaultConfirmCallback) => {
setHasConfirmedTransition(false);
setConfirmTransition(() => ({
defaultConfirmCallback: defaultConfirmCallback,
setHasConfirmedTransition: setHasConfirmedTransition
}));
setIsOpenNavConfirmModal(!isOpenNavConfirmModal);
};

return (
<React.Fragment>
<UserContext.Provider value={account}>
<Router getUserConfirmation={getUserConfirmation}>
<NavConfirmModal
confirmTransition={confirmTransition}
isOpenNavConfirmModal={isOpenNavConfirmModal}
setIsOpenNavConfirmModal={setIsOpenNavConfirmModal}
/>
<App
account={account}
setLoggedInAccount={setLoggedInAccount}
hasConfirmedTransition={hasConfirmedTransition}
/>
</Router>
</UserContext.Provider>
</React.Fragment>
);
};

export default AppWrapper;
34 changes: 34 additions & 0 deletions client/src/hooks/useTracking.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect } from "react";
import { useHistory } from "react-router-dom";

// https://medium.com/javascript-in-plain-english/google-analytics-with-react-router-and-hooks-16d403ddc528

// declare global {
// interface Window {
// gtag?: (
// key: string,
// trackingId: string,
// config: { page_path: string }
// ) => void;
// }
// }

export const useTracking = (trackingId = "G-MW23VES3G6") => {
const { listen } = useHistory();

useEffect(() => {
const unlisten = listen(location => {
if (!window.gtag) return;
if (!trackingId) {
console.error(
"Tracking not enabled, as `trackingId` was not given and there is no `GA_MEASUREMENT_ID`."
);
return;
}

window.gtag("config", trackingId, { page_path: location.pathname });
});

return unlisten;
}, [trackingId, listen]);
};
4 changes: 2 additions & 2 deletions client/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from "react";
import ReactDom from "react-dom";
import App from "./App";
import AppWrapper from "./AppWrapper";
import { ThemeProvider } from "react-jss";
import { jssTheme } from "./styles/theme";

try {
ReactDom.render(
<ThemeProvider theme={jssTheme}>
<App />
<AppWrapper />
</ThemeProvider>,
document.querySelector("#root")
);
Expand Down

0 comments on commit ccd5300

Please sign in to comment.