Skip to content
Snippets Groups Projects
Commit 54ccbb5f authored by xaralis's avatar xaralis
Browse files

Initial login flow

parent 02604c88
No related branches found
No related tags found
No related merge requests found
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.eslintcache
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
......
...@@ -1400,6 +1400,27 @@ ...@@ -1400,6 +1400,27 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
}, },
"@react-keycloak/core": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@react-keycloak/core/-/core-2.2.1.tgz",
"integrity": "sha512-MKoawiLMamhCrcEQ79svZZV8lw+ojkcz4LMGeCzHULlZB8sTDY3uBg7S8NxhpdPdyAjQoQ5ExKL4Il75OBIamg==",
"requires": {
"@babel/runtime": "^7.9.0",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.0.1"
}
},
"@react-keycloak/web": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@react-keycloak/web/-/web-2.1.4.tgz",
"integrity": "sha512-HZry6/UIeStGc2reqYhRFAkmVGCb4fQfOdOK2bJ6RCatN5uuJ+sDu5w5L97JtGNirTqP2eKsTUM3Dx1EvgJ4ng==",
"requires": {
"@babel/runtime": "^7.9.0",
"@react-keycloak/core": "^2.2.1",
"hoist-non-react-statics": "^3.3.2",
"prop-types": "^15.7.2"
}
},
"@sentry/browser": { "@sentry/browser": {
"version": "5.23.0", "version": "5.23.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.23.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.23.0.tgz",
...@@ -1716,7 +1737,8 @@ ...@@ -1716,7 +1737,8 @@
"@testing-library/user-event": { "@testing-library/user-event": {
"version": "7.2.1", "version": "7.2.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz",
"integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==" "integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==",
"dev": true
}, },
"@types/babel__core": { "@types/babel__core": {
"version": "7.1.9", "version": "7.1.9",
...@@ -8192,6 +8214,11 @@ ...@@ -8192,6 +8214,11 @@
} }
} }
}, },
"js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
...@@ -8340,6 +8367,15 @@ ...@@ -8340,6 +8367,15 @@
"object.assign": "^4.1.0" "object.assign": "^4.1.0"
} }
}, },
"keycloak-js": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-10.0.2.tgz",
"integrity": "sha512-7nkg4Ob1khHGcNbuK36AMndKUEuIQFpNlWU9ygWs7nSBPCI9VZ8dJjjXfKJHm0ewgcqLFGPIJ6bxxRlfcQ6sLg==",
"requires": {
"base64-js": "1.3.1",
"js-sha256": "0.9.0"
}
},
"killable": { "killable": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
...@@ -10948,6 +10984,22 @@ ...@@ -10948,6 +10984,22 @@
} }
} }
}, },
"pullstate": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/pullstate/-/pullstate-1.20.4.tgz",
"integrity": "sha512-SksJ70iYNrC+YsGjMx54pXT2/iYYUu3zg9hezjHNjPPwyViglo4lh+N0I4DwB6xw92s+9NagD3AXMGMfiIt76g==",
"requires": {
"fast-deep-equal": "^3.1.3",
"immer": "^7.0.1"
},
"dependencies": {
"immer": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.15.tgz",
"integrity": "sha512-yM7jo9+hvYgvdCQdqvhCNRRio0SCXc8xDPzA25SvKWa7b1WVPjLwQs1VYU5JPXjcJPTqAa5NP5dqpORGYBQ2AA=="
}
}
},
"pump": { "pump": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
...@@ -11313,6 +11365,11 @@ ...@@ -11313,6 +11365,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==" "integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
}, },
"react-fast-compare": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
......
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@react-keycloak/web": "^2.1.4",
"@sentry/react": "^5.23.0", "@sentry/react": "^5.23.0",
"@testing-library/user-event": "^7.2.1",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"keycloak-js": "^10.0.2",
"pullstate": "^1.20.4",
"react": "^16.13.1", "react": "^16.13.1",
"react-device-detect": "^1.13.1", "react-device-detect": "^1.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
...@@ -94,6 +96,7 @@ ...@@ -94,6 +96,7 @@
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/user-event": "^7.2.1",
"@testing-library/react": "^9.5.0", "@testing-library/react": "^9.5.0",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
......
import React from "react"; import React, { useCallback, useEffect } from "react";
import { import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
BrowserRouter as Router, import { KeycloakProvider } from "@react-keycloak/web";
Route,
Switch,
} from "react-router-dom";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { AuthStore } from "stores";
import Footer from "components/Footer"; import Footer from "components/Footer";
import Navbar from "components/Navbar"; import Navbar from "components/Navbar";
import Home from "pages/Home"; import Home from "pages/Home";
import ForVisitors from "pages/ForVisitors";
import ForPress from "pages/ForPress";
import Program from "pages/Program"; import Program from "pages/Program";
import keycloak from "./keycloak";
/** /**
* If configured, set up Sentry client that reports uncaught errors down to * If configured, set up Sentry client that reports uncaught errors down to
* https://sentry.io. * https://sentry.io.
...@@ -25,19 +22,65 @@ if (process.env.REACT_APP_SENTRY_DSN) { ...@@ -25,19 +22,65 @@ if (process.env.REACT_APP_SENTRY_DSN) {
}); });
} }
const onKeycloakEvent = (event) => {
if (["onAuthRefreshSuccess", "onAuthSuccess"].includes(event)) {
Sentry.setUser(keycloak.tokenParsed);
AuthStore.update((state) => {
state.isAuthenticated = true;
state.user = {
name: keycloak.tokenParsed.name,
groups: keycloak.tokenParsed.groups,
};
});
}
};
const LoadingComponent = <p className="block mt-4 font-bold">Načítání ...</p>;
const BaseApp = () => { const BaseApp = () => {
const loadGroupMappings = useCallback(async () => {
const resp = await fetch("https://iapi.pirati.cz/v1/groups");
const mappings = await resp.json();
AuthStore.update((state) => {
state.groupMappings = mappings;
});
}, []);
useEffect(() => {
loadGroupMappings();
}, [loadGroupMappings]);
return ( return (
<>
<Router> <Router>
<Navbar /> <Navbar />
<Switch> <Switch>
<Route exact path="/" children={<Home />} /> <Route exact path="/" children={<Home />} />
<Route exact path="/program" children={<Program />} /> <Route exact path="/program" children={<Program />} />
<Route exact path="/pro-ucastniky" children={<ForVisitors />} />
<Route exact path="/media" children={<ForPress />} />
</Switch> </Switch>
<Footer /> <Footer />
</Router> </Router>
);
};
const AuthenticatedApp = () => {
const keycloakInitConfig = {
onLoad: "check-sso",
// Necessary to prevent Keycloak cookie issues:
// @see: https://stackoverflow.com/a/63588334/303184
checkLoginIframe: false,
};
return (
<>
<KeycloakProvider
keycloak={keycloak}
initConfig={keycloakInitConfig}
LoadingComponent={LoadingComponent}
onEvent={onKeycloakEvent}
>
<BaseApp />
</KeycloakProvider>
</> </>
); );
}; };
...@@ -61,7 +104,7 @@ const ErrorBoundaryFallback = () => ( ...@@ -61,7 +104,7 @@ const ErrorBoundaryFallback = () => (
const App = Sentry.withProfiler(() => { const App = Sentry.withProfiler(() => {
return ( return (
<Sentry.ErrorBoundary fallback={ErrorBoundaryFallback} showDialog> <Sentry.ErrorBoundary fallback={ErrorBoundaryFallback} showDialog>
<BaseApp /> <AuthenticatedApp />
</Sentry.ErrorBoundary> </Sentry.ErrorBoundary>
); );
}); });
......
import React from 'react'; import React from "react";
import { render } from '@testing-library/react'; import { render } from "@testing-library/react";
import App from './App';
test('renders learn react link', () => { import App from "./App";
test("renders learn react link", () => {
const { getByText } = render(<App />); const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i); const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument(); expect(linkElement).toBeInTheDocument();
......
import React from "react";
import classNames from "classnames";
const Button = ({
className,
icon,
hoverActive = true,
fullwidth = false,
children,
...props
}) => {
const btnClass = classNames(
"btn",
{
"btn--icon": !!icon,
"btn--hoveractive": hoverActive,
"btn--fullwidth md:btn--autowidth": fullwidth,
},
className
);
return (
<button className={btnClass} {...props}>
<div className="btn__body-wrap">
<div className="btn__body ">{children}</div>
{!!icon && (
<div className="btn__icon">
<i className={icon}></i>
</div>
)}
</div>
</button>
);
};
export default Button;
...@@ -15,68 +15,7 @@ const Footer = () => { ...@@ -15,68 +15,7 @@ const Footer = () => {
sdílet za stejných podmínek. sdílet za stejných podmínek.
</p> </p>
</section> </section>
<section className="footer__main-links bg-grey-700 text-white lg:grid grid-cols-3 gap-4"> <section className="footer__main-links bg-grey-700 text-white lg:grid grid-cols-3 gap-4"></section>
<div className="pt-4 lg:py-0 border-t border-grey-400 lg:border-t-0">
<div className="footer-collapsible">
<span className="text-xl uppercase text-white footer-collapsible__toggle">
Program
</span>{" "}
<div className="">
<ul className="mt-6 space-y-2 text-grey-200">
<li>
<a href="#">Pátek</a>
</li>{" "}
<li>
<a href="#">Sobota</a>
</li>{" "}
<li>
<a href="#">Neděle</a>
</li>
</ul>
</div>
</div>
</div>
<div className="pt-8 pb-4 lg:py-0">
<div className="footer-collapsible">
<span className="text-xl uppercase text-white footer-collapsible__toggle">
Pro účastníky
</span>{" "}
<div className="">
<ul className="mt-6 space-y-2 text-grey-200">
<li>
<a href="#">Registrace účastníků</a>
</li>{" "}
<li>
<a href="#">Kudy na fórum</a>
</li>
<li>
<a href="#">Ubytování</a>
</li>
</ul>
</div>
</div>
</div>{" "}
<div className="py-4 lg:py-0 border-t border-grey-400 lg:border-t-0">
<div className="footer-collapsible">
<span className="text-xl uppercase text-white footer-collapsible__toggle">
Pro média
</span>{" "}
<div className="">
<ul className="mt-6 space-y-2 text-grey-200">
<li>
<a href="#">Registrace novinářů</a>
</li>{" "}
<li>
<a href="#">Informace pro novináře</a>
</li>{" "}
<li>
<a href="#">Presskit</a>
</li>
</ul>
</div>
</div>
</div>{" "}
</section>
<section className="footer__social lg:text-right"> <section className="footer__social lg:text-right">
<div className="flex flex-col md:flex-row lg:flex-col lg:items-end space-y-2 md:space-y-0 md:space-x-2 lg:space-x-0 lg:space-y-2"> <div className="flex flex-col md:flex-row lg:flex-col lg:items-end space-y-2 md:space-y-0 md:space-x-2 lg:space-x-0 lg:space-y-2">
<button className="btn btn--icon btn--blue-300 btn--hoveractive text-lg btn--fullwidth sm:btn--autowidth"> <button className="btn btn--icon btn--blue-300 btn--hoveractive text-lg btn--fullwidth sm:btn--autowidth">
......
import React from "react";
import { NavLink } from "react-router-dom";
const HomeHero = () => {
return (
<article
className="hero py-24"
>
<div className="container container--default text-center">
<h1 className="head-alt-lg mb-4">12. Celostátní fórum</h1>
<h2 className="head-alt-xl">České pirátské strany</h2>
<p className="head-heavy-base mt-4">11. - 12. ledna, Pardubice</p>
<p className="head-heavy-sm">Kulturní centrum IDEON</p>
<div className="mt-4 md:mt-8 space-x-4">
<NavLink className="btn btn--blue-300 btn--icon btn--hoveractive btn--fullwidth md:btn--autowidth text-xl" to="/registrace">
<div className="btn__body-wrap">
<div className="btn__body ">Registrace</div>
<div className="btn__icon ">
<i className="ico--book"></i>
</div>
</div>
</NavLink>
<NavLink className="btn btn--violet-400 btn--icon btn--hoveractive btn--fullwidth md:btn--autowidth text-xl" to="/program">
<div className="btn__body-wrap">
<div className="btn__body ">Program</div>
<div className="btn__icon ">
<i className="ico--calendar"></i>
</div>
</div>
</NavLink>
</div>
</div>
</article>
);
};
export default HomeHero;
import React, { useState } from "react"; import React, { useCallback, useState } from "react";
import { isBrowser } from "react-device-detect"; import { isBrowser } from "react-device-detect";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { useKeycloak } from "@react-keycloak/web";
import { AuthStore } from "stores";
import Button from "components/Button";
const Navbar = () => { const Navbar = () => {
const [showMenu, setShowMenu] = useState(isBrowser); const [showMenu, setShowMenu] = useState(isBrowser);
const { keycloak } = useKeycloak();
const { isAuthenticated, user } = AuthStore.useState();
const login = useCallback(() => {
keycloak.login();
}, [keycloak]);
const logout = useCallback(() => {
keycloak.logout();
}, [keycloak]);
return ( return (
<nav className="navbar navbar--simple"> <nav className="navbar navbar--simple">
...@@ -13,48 +26,55 @@ const Navbar = () => { ...@@ -13,48 +26,55 @@ const Navbar = () => {
<img <img
src={`${process.env.REACT_APP_STYLEGUIDE_URL}/images/logo-round-white.svg`} src={`${process.env.REACT_APP_STYLEGUIDE_URL}/images/logo-round-white.svg`}
className="w-8" className="w-8"
alt="Pirátská strana"
/> />
</NavLink> </NavLink>
<NavLink to="/" className="pl-4 font-bold text-xl lg:border-r lg:border-grey-300 lg:pr-8 hover:no-underline"> <NavLink
to="/"
className="pl-4 font-bold text-xl lg:border-r lg:border-grey-300 lg:pr-8 hover:no-underline"
>
Celostátní fórum 2021 Celostátní fórum 2021
</NavLink> </NavLink>
</div> </div>
<div className="navbar__menutoggle my-4 flex justify-end lg:hidden"> <div className="navbar__menutoggle my-4 flex justify-end lg:hidden">
<a <button
href="#"
onClick={() => setShowMenu(!showMenu)} onClick={() => setShowMenu(!showMenu)}
className="no-underline hover:no-underline" className="no-underline hover:no-underline"
> >
<i className="ico--menu text-3xl"></i> <i className="ico--menu text-3xl"></i>
</a> </button>
</div> </div>
{showMenu && ( {showMenu && (
<> <>
<div className="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto"> <div className="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto">
<ul className="navbar-menu text-white"> <ul className="navbar-menu text-white">
<li className="navbar-menu__item"> <li className="navbar-menu__item">
<NavLink className="navbar-menu__link" to="/program">Program</NavLink> <NavLink className="navbar-menu__link" to="/program">
</li> Program
<li className="navbar-menu__item"> </NavLink>
<NavLink className="navbar-menu__link" to="/registrace">Registrace</NavLink>
</li>
<li className="navbar-menu__item">
<NavLink className="navbar-menu__link" to="/pro-ucastniky">Pro účastníky</NavLink>
</li>
<li className="navbar-menu__item">
<NavLink className="navbar-menu__link" to="/media">Pro média</NavLink>
</li> </li>
</ul> </ul>
</div> </div>
<div className="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-col sm:flex-row lg:flex-col sm:space-x-4 space-y-2 sm:space-y-0 lg:space-y-2 xl:flex-row xl:space-x-2 xl:space-y-0"> <div className="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-col sm:flex-row lg:flex-col sm:space-x-4 space-y-2 sm:space-y-0 lg:space-y-2 xl:flex-row xl:space-x-2 xl:space-y-0">
<NavLink className="btn btn--white btn--icon btn--hoveractive btn--fullwidth md:btn--autowidth text-sm" to="/program"> {!isAuthenticated && (
<div className="btn__body-wrap"> <Button className="btn--white" onClick={login}>
<div className="btn__body ">Osobní zóna</div> Přihlásit se
<div className="btn__icon"> </Button>
<i className="ico--users"></i> )}
{isAuthenticated && (
<div className="flex items-center space-x-4">
<span className="head-heavy-2xs">{user.name}</span>
<div className="avatar avatar--2xs">
<img
src="http://placeimg.com/100/100/people"
alt="Avatar"
/>
</div> </div>
<button onClick={logout}>
<i className="ico--log-out"></i>
</button>
</div> </div>
</NavLink> )}
</div> </div>
</> </>
)} )}
......
export default { export default {
styleguideUrl: "https://styleguide.pir-test.eu/latest" styleguideUrl: "https://styleguide.pir-test.eu/latest",
}; };
.h-screen {
min-height: 100vh;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom'; import ReactDOM from "react-dom";
import './index.css';
import App from './App'; import App from "./App";
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from "./serviceWorker";
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById("root")
); );
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
......
import Keycloak from "keycloak-js";
// Setup Keycloak instance as needed
// Pass initialization options as required or leave blank to load from 'keycloak.json'
const keycloak = Keycloak({
url: "https://auth.pirati.cz/auth",
realm: "pirati",
clientId: "cf-online",
});
// keycloak.init({
// onLoad: "check-sso",
// });
export default keycloak;
import React from "react";
const ForPress = () => {
return (
<>
Press
</>
);
};
export default ForPress;
import React from "react";
const ForVisitors = () => {
return (
<>
Visitors
</>
);
};
export default ForVisitors;
import React from "react"; import React from "react";
import HomeHero from "components/HomeHero";
const Home = () => { const Home = () => {
return ( return (
<> <>
<HomeHero /> <p>Homepage</p>
<hr />
<section className="container container--default my-8 text-center">
<h1 className="head-alt-base mb-4">Celostátní fórum</h1>
<p>Celostátní fórum Pirátské strany je nejvyšším orgánem strany a zasedání se podle možností účastní každý člen strany.</p>
</section>
<hr />
<section className="container container--default my-8 text-center">
<h1 className="head-alt-base mb-4">Základní informace</h1>
<p>Celostátní fórum Pirátské strany je nejvyšším orgánem strany a zasedání se podle možností účastní každý člen strany.</p>
</section>
</> </>
); );
}; };
......
import React from "react"; import React from "react";
const Schedule = () => { const Schedule = () => {
return ( return <>Schedule</>;
<>
Schedule
</>
);
}; };
export default Schedule; export default Schedule;
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
// opt-in, read https://bit.ly/CRA-PWA // opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean( const isLocalhost = Boolean(
window.location.hostname === 'localhost' || window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4. // 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match( window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
...@@ -21,7 +21,7 @@ const isLocalhost = Boolean( ...@@ -21,7 +21,7 @@ const isLocalhost = Boolean(
); );
export function register(config) { export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW. // The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) { if (publicUrl.origin !== window.location.origin) {
...@@ -31,7 +31,7 @@ export function register(config) { ...@@ -31,7 +31,7 @@ export function register(config) {
return; return;
} }
window.addEventListener('load', () => { window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) { if (isLocalhost) {
...@@ -42,8 +42,8 @@ export function register(config) { ...@@ -42,8 +42,8 @@ export function register(config) {
// service worker/PWA documentation. // service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
'This web app is being served cache-first by a service ' + "This web app is being served cache-first by a service " +
'worker. To learn more, visit https://bit.ly/CRA-PWA' "worker. To learn more, visit https://bit.ly/CRA-PWA"
); );
}); });
} else { } else {
...@@ -57,21 +57,21 @@ export function register(config) { ...@@ -57,21 +57,21 @@ export function register(config) {
function registerValidSW(swUrl, config) { function registerValidSW(swUrl, config) {
navigator.serviceWorker navigator.serviceWorker
.register(swUrl) .register(swUrl)
.then(registration => { .then((registration) => {
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing;
if (installingWorker == null) { if (installingWorker == null) {
return; return;
} }
installingWorker.onstatechange = () => { installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') { if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) { if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched, // At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older // but the previous service worker will still serve the older
// content until all client tabs are closed. // content until all client tabs are closed.
console.log( console.log(
'New content is available and will be used when all ' + "New content is available and will be used when all " +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.' "tabs for this page are closed. See https://bit.ly/CRA-PWA."
); );
// Execute callback // Execute callback
...@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) { ...@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
// At this point, everything has been precached. // At this point, everything has been precached.
// It's the perfect time to display a // It's the perfect time to display a
// "Content is cached for offline use." message. // "Content is cached for offline use." message.
console.log('Content is cached for offline use.'); console.log("Content is cached for offline use.");
// Execute callback // Execute callback
if (config && config.onSuccess) { if (config && config.onSuccess) {
...@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) { ...@@ -93,25 +93,25 @@ function registerValidSW(swUrl, config) {
}; };
}; };
}) })
.catch(error => { .catch((error) => {
console.error('Error during service worker registration:', error); console.error("Error during service worker registration:", error);
}); });
} }
function checkValidServiceWorker(swUrl, config) { function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, { fetch(swUrl, {
headers: { 'Service-Worker': 'script' }, headers: { "Service-Worker": "script" },
}) })
.then(response => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type'); const contentType = response.headers.get("content-type");
if ( if (
response.status === 404 || response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1) (contentType != null && contentType.indexOf("javascript") === -1)
) { ) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload();
}); });
...@@ -123,18 +123,18 @@ function checkValidServiceWorker(swUrl, config) { ...@@ -123,18 +123,18 @@ function checkValidServiceWorker(swUrl, config) {
}) })
.catch(() => { .catch(() => {
console.log( console.log(
'No internet connection found. App is running in offline mode.' "No internet connection found. App is running in offline mode."
); );
}); });
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready navigator.serviceWorker.ready
.then(registration => { .then((registration) => {
registration.unregister(); registration.unregister();
}) })
.catch(error => { .catch((error) => {
console.error(error.message); console.error(error.message);
}); });
} }
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
// allows you to do things like: // allows you to do things like:
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'; import "@testing-library/jest-dom/extend-expect";
import { Store } from "pullstate";
export const AuthStore = new Store({
isAuthenticated: false,
user: {
name: null,
groups: null,
},
groupMappings: null,
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment