├── public ├── robots.txt ├── favicon.ico ├── favicon.png ├── logo192.png ├── logo512.png ├── favicon-32.png ├── favicon-128.png ├── favicon-152.png ├── favicon-167.png ├── favicon-180.png ├── favicon-192.png ├── favicon-196.png ├── manifest.json └── index.html ├── src ├── pages │ ├── Stepper │ │ ├── constants.js │ │ ├── index.js │ │ ├── components │ │ │ ├── ThirdStep │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── StepperPage │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── FirstStep │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── SecondStep │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ └── TextField │ │ │ │ └── index.jsx │ │ └── services │ │ │ ├── clearFormData.js │ │ │ ├── setFormData.js │ │ │ └── getFormData.js │ ├── Home │ │ ├── index.jsx │ │ └── components │ │ │ └── HomePage │ │ │ ├── styles.js │ │ │ └── index.jsx │ ├── ClosePopup │ │ ├── components │ │ │ ├── CustomSessionHistory │ │ │ │ ├── constants.js │ │ │ │ ├── services │ │ │ │ │ ├── clearCustomHistory.js │ │ │ │ │ ├── getHasPrevRoute.js │ │ │ │ │ ├── setCustomHistory.js │ │ │ │ │ ├── getCustomHistory.js │ │ │ │ │ └── updateSessionHistory.js │ │ │ │ ├── hooks │ │ │ │ │ └── useClosePopup.js │ │ │ │ ├── HistoryLogger │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ ├── CustomReduxHistory │ │ │ │ ├── selectors │ │ │ │ │ └── history.js │ │ │ │ ├── hooks │ │ │ │ │ ├── useGoBack.js │ │ │ │ │ ├── usePush.js │ │ │ │ │ ├── useReplace.js │ │ │ │ │ └── useClosePopup.js │ │ │ │ └── index.js │ │ │ ├── State │ │ │ │ ├── hooks │ │ │ │ │ ├── usePreparePopupLink.js │ │ │ │ │ └── useClosePopup.js │ │ │ │ └── index.js │ │ │ ├── ClosePopupPage │ │ │ │ ├── styles.js │ │ │ │ └── index.js │ │ │ ├── GoBack │ │ │ │ └── index.js │ │ │ └── Push │ │ │ │ └── index.js │ │ └── index.js │ ├── TwoPopups │ │ ├── index.js │ │ ├── components │ │ │ ├── Post │ │ │ │ ├── styles.js │ │ │ │ ├── PostCard │ │ │ │ │ ├── styles.js │ │ │ │ │ ├── SimilarPicturesPopup │ │ │ │ │ │ ├── OriginalImage │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── ViewersPopup │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── LikesPopup │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ └── index.jsx │ │ │ │ └── index.jsx │ │ │ ├── GetParameterPopups │ │ │ │ ├── Popups │ │ │ │ │ ├── SignIn │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── SignUp │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── NotificationDetails │ │ │ │ │ │ ├── Details │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ └── index.js │ │ │ │ │ └── Notifications │ │ │ │ │ │ ├── NotificationsList │ │ │ │ │ │ ├── NotificationItem │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ └── index.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── hooks │ │ │ │ │ └── useGetPopupsState.js │ │ │ ├── Posts │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ └── TwoPopupsPage │ │ │ │ └── index.jsx │ │ └── data │ │ │ └── notifications.json │ ├── CustomPrompt │ │ ├── index.js │ │ └── components │ │ │ ├── CustomPromptPage │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── SignUpForm │ │ │ ├── styles.js │ │ │ ├── usePreventReload.js │ │ │ ├── useFormState.js │ │ │ └── index.jsx │ │ │ └── SignUpButton │ │ │ └── index.jsx │ ├── PopupsRoutes │ │ ├── index.js │ │ ├── components │ │ │ ├── GetParameterPopups │ │ │ │ ├── Popups │ │ │ │ │ ├── Notifications │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ ├── NotificationsList │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ ├── NotificationItem │ │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── NotificationDetails │ │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── SignIn │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ └── SignUp │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── hooks │ │ │ │ │ └── useGetPopupState.js │ │ │ ├── Post │ │ │ │ ├── styles.js │ │ │ │ ├── PostCard │ │ │ │ │ ├── styles.js │ │ │ │ │ ├── ViewersPopup │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── LikesPopup │ │ │ │ │ │ ├── styles.js │ │ │ │ │ │ └── index.jsx │ │ │ │ │ └── index.jsx │ │ │ │ └── index.jsx │ │ │ ├── Posts │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ └── PopupsRoutesPage │ │ │ │ └── index.jsx │ │ └── data │ │ │ ├── notifications.json │ │ │ └── posts.json │ ├── ResponsiveRoutes │ │ ├── index.jsx │ │ └── components │ │ │ ├── Filter │ │ │ ├── styles.js │ │ │ ├── Make │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── Model │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── Body │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── Enging │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── Miles │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ ├── Year │ │ │ │ ├── styles.js │ │ │ │ └── index.jsx │ │ │ └── index.jsx │ │ │ ├── ResponsiveFilter │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── ResponsiveList │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── Sidebar │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── ResponsiveFilters │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── Desktop │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ ├── List │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ │ └── ResponsiveRoutesPage │ │ │ └── index.jsx │ └── RestorePreventedRoute │ │ ├── index.js │ │ ├── components │ │ ├── RestorePreventedRoutePage │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── AuthStatus │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── Links │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── Login │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── Books │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── Food │ │ │ ├── styles.js │ │ │ └── index.jsx │ │ ├── AuthRoute │ │ │ └── index.jsx │ │ └── Gallery │ │ │ └── index.jsx │ │ └── data │ │ ├── books.json │ │ └── gallery.json ├── contexts │ └── DeviceInfoContext.js ├── assets │ ├── images │ │ ├── close-popup.png │ │ ├── popus-routes.png │ │ ├── two-popups.png │ │ ├── custom-prompt.png │ │ ├── multi-step-form.png │ │ ├── responsive-routes.png │ │ └── redirect-after-login.png │ └── icons │ │ ├── youtube.svg │ │ └── github.svg ├── index.js ├── reducer │ ├── index.js │ └── history.js ├── hooks │ └── router │ │ ├── useGetParameter.js │ │ └── usePrepareLink.js ├── actions │ └── history.js ├── components │ ├── App │ │ ├── styles.js │ │ └── index.jsx │ ├── AppBar │ │ ├── styles.js │ │ └── index.jsx │ ├── DeviceInfoHandler │ │ └── index.jsx │ ├── CustomPrompt │ │ └── index.jsx │ └── Router │ │ └── index.jsx ├── const │ ├── router.js │ └── tricks.js ├── store.js └── services │ └── calculateDeviceInfo.js ├── config ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── pnpTs.js ├── paths.js ├── env.js └── modules.js ├── .gitignore ├── .github └── workflows │ ├── master-deployment.yml │ └── develop-deployment.yml ├── scripts ├── test.js └── start.js └── package.json /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /src/pages/Stepper/constants.js: -------------------------------------------------------------------------------- 1 | export const STEPPER_FORM_DATA_KEY = "stepper-form-data"; 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-32.png -------------------------------------------------------------------------------- /src/pages/Home/index.jsx: -------------------------------------------------------------------------------- 1 | import HomePage from "./components/HomePage"; 2 | 3 | export default HomePage; 4 | -------------------------------------------------------------------------------- /public/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-128.png -------------------------------------------------------------------------------- /public/favicon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-152.png -------------------------------------------------------------------------------- /public/favicon-167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-167.png -------------------------------------------------------------------------------- /public/favicon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-180.png -------------------------------------------------------------------------------- /public/favicon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-192.png -------------------------------------------------------------------------------- /public/favicon-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/public/favicon-196.png -------------------------------------------------------------------------------- /src/contexts/DeviceInfoContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default React.createContext(); 4 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/constants.js: -------------------------------------------------------------------------------- 1 | export const CUSTOM_SESSION_KEY = "custom-history"; 2 | -------------------------------------------------------------------------------- /src/pages/Stepper/index.js: -------------------------------------------------------------------------------- 1 | import StepperPage from "./components/StepperPage"; 2 | 3 | export default StepperPage; 4 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/index.js: -------------------------------------------------------------------------------- 1 | import TwoPopupsPage from "./components/TwoPopupsPage"; 2 | 3 | export default TwoPopupsPage; 4 | -------------------------------------------------------------------------------- /src/assets/images/close-popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/close-popup.png -------------------------------------------------------------------------------- /src/assets/images/popus-routes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/popus-routes.png -------------------------------------------------------------------------------- /src/assets/images/two-popups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/two-popups.png -------------------------------------------------------------------------------- /src/pages/ClosePopup/index.js: -------------------------------------------------------------------------------- 1 | import ClosePopupPage from "./components/ClosePopupPage"; 2 | 3 | export default ClosePopupPage; 4 | -------------------------------------------------------------------------------- /src/assets/images/custom-prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/custom-prompt.png -------------------------------------------------------------------------------- /src/assets/images/multi-step-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/multi-step-form.png -------------------------------------------------------------------------------- /src/assets/images/responsive-routes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/responsive-routes.png -------------------------------------------------------------------------------- /src/pages/CustomPrompt/index.js: -------------------------------------------------------------------------------- 1 | import CustomPromptPage from "./components/CustomPromptPage"; 2 | 3 | export default CustomPromptPage; 4 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/index.js: -------------------------------------------------------------------------------- 1 | import PopupsRoutesPage from "./components/PopupsRoutesPage"; 2 | 3 | export default PopupsRoutesPage; 4 | -------------------------------------------------------------------------------- /src/assets/images/redirect-after-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin9k/react-router-dom-tricks/HEAD/src/assets/images/redirect-after-login.png -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/index.jsx: -------------------------------------------------------------------------------- 1 | import ResponsiveRoutesPage from "./components/ResponsiveRoutesPage"; 2 | 3 | export default ResponsiveRoutesPage; 4 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/index.js: -------------------------------------------------------------------------------- 1 | import RestorePreventedRoutePage from "./components/RestorePreventedRoutePage"; 2 | 3 | export default RestorePreventedRoutePage; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import App from "./components/App"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/ThirdStep/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | label: { 5 | fontSize: 20, 6 | }, 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | } 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/StepperPage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | }, 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Make/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | } 7 | })); 8 | -------------------------------------------------------------------------------- /src/reducer/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | 3 | import history from "./history"; 4 | 5 | const reducer = combineReducers({ 6 | history 7 | }); 8 | 9 | export default reducer; 10 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/CustomPromptPage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | } 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Model/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | } 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/Stepper/services/clearFormData.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { STEPPER_FORM_DATA_KEY } from "../constants"; 3 | 4 | export default () => { 5 | sessionStorage.removeItem(STEPPER_FORM_DATA_KEY); 6 | }; 7 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveFilter/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | button: { 5 | margin: "16px 16px 0" 6 | } 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Body/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | width: 200, 6 | height: 80 7 | } 8 | })); 9 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/FirstStep/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | field: { 5 | display: "block", 6 | marginBottom: 16, 7 | }, 8 | })); 9 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Enging/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | width: 200, 6 | height: 80 7 | } 8 | })); 9 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/RestorePreventedRoutePage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | }, 7 | })); 8 | -------------------------------------------------------------------------------- /src/pages/Stepper/services/setFormData.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { STEPPER_FORM_DATA_KEY } from "../constants"; 3 | 4 | export default (formData) => { 5 | sessionStorage.setItem(STEPPER_FORM_DATA_KEY, JSON.stringify(formData)); 6 | }; 7 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/services/clearCustomHistory.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { CUSTOM_SESSION_KEY } from "../constants"; 3 | 4 | export default () => { 5 | sessionStorage.removeItem(CUSTOM_SESSION_KEY); 6 | }; 7 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/services/getHasPrevRoute.js: -------------------------------------------------------------------------------- 1 | import getCustomHistory from "./getCustomHistory"; 2 | 3 | export default () => { 4 | const customHistory = getCustomHistory(); 5 | 6 | return customHistory.length > 0; 7 | }; 8 | -------------------------------------------------------------------------------- /src/hooks/router/useGetParameter.js: -------------------------------------------------------------------------------- 1 | /* global URLSearchParams */ 2 | import { useLocation } from "react-router-dom"; 3 | 4 | export default name => { 5 | const { search } = useLocation(); 6 | const query = new URLSearchParams(search); 7 | return query.get(name); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveList/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | }, 7 | filters: { 8 | marginBottom: 16 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/AuthStatus/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | authorized: { 5 | color: "green" 6 | }, 7 | notAuthorized: { 8 | color: "red" 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/selectors/history.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from "reselect"; 2 | 3 | export const getHistory = state => state.history; 4 | 5 | export const getHasPrevRoute = createSelector( 6 | getHistory, 7 | history => history.length > 0 8 | ); 9 | -------------------------------------------------------------------------------- /src/actions/history.js: -------------------------------------------------------------------------------- 1 | import { createAction } from "redux-actions"; 2 | 3 | const NAMESPACE = "HISTORY::"; 4 | 5 | export const push = createAction(NAMESPACE + "PUSH"); 6 | export const replace = createAction(NAMESPACE + "REPLACE"); 7 | export const goBack = createAction(NAMESPACE + "GO_BACK"); 8 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/services/setCustomHistory.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { CUSTOM_SESSION_KEY } from "../constants"; 3 | 4 | export default customHistory => { 5 | sessionStorage.setItem(CUSTOM_SESSION_KEY, JSON.stringify(customHistory)); 6 | }; 7 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/State/hooks/usePreparePopupLink.js: -------------------------------------------------------------------------------- 1 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 2 | 3 | export default linkData => { 4 | return usePrepareLink({ 5 | ...linkData, 6 | state: { 7 | hasPrevRoute: true 8 | } 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/SecondStep/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | backIcon: { 5 | marginBottom: 16, 6 | }, 7 | field: { 8 | display: "block", 9 | marginBottom: 16, 10 | }, 11 | })); 12 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | dialog: { 5 | maxWidth: 650 6 | }, 7 | container: { 8 | display: "flex" 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Miles/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | width: 200, 6 | height: 60, 7 | display: "flex", 8 | alignItems: "flex-end" 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Year/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | width: 200, 6 | height: 60, 7 | display: "flex", 8 | alignItems: "flex-end" 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Sidebar/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | title: { 5 | padding: "0 16px" 6 | }, 7 | container: { 8 | background: "#f6f6f6", 9 | padding: 0 10 | } 11 | })); 12 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | }, 7 | back: { 8 | marginBottom: 16 9 | }, 10 | button: { 11 | marginRight: 16 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | }, 7 | back: { 8 | marginBottom: 16 9 | }, 10 | button: { 11 | marginRight: 16 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | card: { 5 | maxWidth: 345, 6 | margin: "0 16px 16px 0", 7 | textDecoration: "none" 8 | }, 9 | media: { 10 | height: 140 11 | } 12 | })); 13 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | card: { 5 | maxWidth: 345, 6 | margin: "0 16px 16px 0", 7 | textDecoration: "none" 8 | }, 9 | media: { 10 | height: 140 11 | } 12 | })); 13 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Links/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | link: { 5 | fontSize: 20 6 | }, 7 | active: { 8 | pointerEvents: "none", 9 | textDecoration: "none", 10 | color: "black" 11 | } 12 | })); 13 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Login/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | button: { 5 | marginTop: 16 6 | }, 7 | nextBooks: { 8 | color: "#9C51B6" 9 | }, 10 | nextFood: { 11 | color: "#2243B6" 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationsList/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | root: { 5 | width: 300, 6 | backgroundColor: theme.palette.background.paper, 7 | flexShrink: 0 8 | } 9 | })); 10 | -------------------------------------------------------------------------------- /src/components/App/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | "@global": { 5 | body: { 6 | margin: 0, 7 | backgroundColor: "#F1F1F1", 8 | }, 9 | }, 10 | container: { 11 | minWidth: "100vw", 12 | minHeight: "100vh", 13 | }, 14 | })); 15 | -------------------------------------------------------------------------------- /src/const/router.js: -------------------------------------------------------------------------------- 1 | export const GET_PARAMS = { 2 | popup: "popup", 3 | notificationId: "notification-id", 4 | }; 5 | 6 | export const GET_ENUMS = { 7 | popup: { 8 | signIn: "sign-in", 9 | signUp: "sign-up", 10 | notifications: "notifications", 11 | notificationDetails: "notification-details", 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/pages/Stepper/services/getFormData.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { STEPPER_FORM_DATA_KEY } from "../constants"; 3 | 4 | export default () => { 5 | const rawFormData = sessionStorage.getItem(STEPPER_FORM_DATA_KEY); 6 | 7 | if (!rawFormData) { 8 | return []; 9 | } 10 | 11 | return JSON.parse(rawFormData); 12 | }; 13 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/SimilarPicturesPopup/OriginalImage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 0, 6 | border: "none", 7 | cursor: "pointer", 8 | height: 400, 9 | overflow: "hidden", 10 | }, 11 | })); 12 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveFilters/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | button: { 5 | margin: "16px 16px 0" 6 | }, 7 | title: { 8 | padding: "0 16px" 9 | }, 10 | container: { 11 | background: "#f6f6f6", 12 | padding: 0 13 | } 14 | })); 15 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/SignIn/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | display: "flex", 7 | flexDirection: "column", 8 | minWidth: 400 9 | }, 10 | field: { 11 | marginBottom: 20 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/SignUp/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | display: "flex", 7 | flexDirection: "column", 8 | minWidth: 400 9 | }, 10 | field: { 11 | marginBottom: 20 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/SignIn/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | display: "flex", 7 | flexDirection: "column", 8 | minWidth: 400 9 | }, 10 | field: { 11 | marginBottom: 20 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/SignUp/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | display: "flex", 7 | flexDirection: "column", 8 | minWidth: 400 9 | }, 10 | field: { 11 | marginBottom: 20 12 | } 13 | })); 14 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationsList/NotificationItem/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | inline: { 5 | display: "inline" 6 | }, 7 | item: { 8 | "&:hover": { 9 | backgroundColor: "#f0f0f0" 10 | } 11 | } 12 | })); 13 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/services/getCustomHistory.js: -------------------------------------------------------------------------------- 1 | /* global sessionStorage */ 2 | import { CUSTOM_SESSION_KEY } from "../constants"; 3 | 4 | export default () => { 5 | const rawCustomHistory = sessionStorage.getItem(CUSTOM_SESSION_KEY); 6 | 7 | if (!rawCustomHistory) { 8 | return []; 9 | } 10 | 11 | return JSON.parse(rawCustomHistory); 12 | }; 13 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/SignUpForm/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | display: "flex", 7 | flexDirection: "column", 8 | minWidth: 400 9 | }, 10 | mobileContainer: { 11 | minWidth: 240 12 | }, 13 | field: { 14 | marginBottom: 20 15 | } 16 | })); 17 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, compose, createStore } from "redux"; 2 | 3 | import reducer from "./reducer"; 4 | 5 | export default () => { 6 | const middlewares = applyMiddleware(); 7 | 8 | const composeEnhancers = 9 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 10 | 11 | const store = createStore(reducer, composeEnhancers(middlewares)); 12 | 13 | return { store }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Desktop/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | maxWidth: 1024, 6 | display: "flex", 7 | padding: 16, 8 | margin: "0 auto" 9 | }, 10 | sidebar: { 11 | flex: "1 0 25%" 12 | }, 13 | list: { 14 | paddingLeft: 16, 15 | flex: "3 0 75%" 16 | } 17 | })); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/TextField/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import MaterialTextField from "@material-ui/core/TextField"; 4 | 5 | const TextField = ({ input, meta, className, label, variant }) => { 6 | return ( 7 | 13 | ); 14 | }; 15 | 16 | export default TextField; 17 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/ViewersPopup/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | padding: 16, 6 | minWidth: 400 7 | }, 8 | paper: { 9 | maxWidth: 400, 10 | margin: `${theme.spacing(1)}px auto`, 11 | padding: theme.spacing(2) 12 | }, 13 | title: { 14 | display: "flex", 15 | alignItems: "center" 16 | } 17 | })); 18 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/List/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | color: "#444", 6 | backgroundColor: "#f6f6f6", 7 | cursor: "pointer", 8 | padding: "16px 0", 9 | marginBottom: 10, 10 | 11 | "&:hover": { 12 | boxShadow: "0 3px 6px 0 rgba(0,0,0,.35)" 13 | } 14 | }, 15 | list: { 16 | margin: 0 17 | } 18 | })); 19 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/ViewersPopup/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | padding: 16, 6 | minWidth: 400 7 | }, 8 | paper: { 9 | maxWidth: 400, 10 | margin: `${theme.spacing(1)}px auto`, 11 | padding: theme.spacing(2) 12 | }, 13 | title: { 14 | display: "flex", 15 | alignItems: "center" 16 | } 17 | })); 18 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationDetails/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | borderLeft: "1px solid rgba(0, 0, 0, 0.12)" 6 | }, 7 | large: { 8 | margin: "24px auto", 9 | width: 84, 10 | height: 84 11 | }, 12 | title: { 13 | marginBottom: 12, 14 | textAlign: "center" 15 | } 16 | })); 17 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/NotificationDetails/Details/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles((theme) => ({ 4 | container: { 5 | borderLeft: "1px solid rgba(0, 0, 0, 0.12)", 6 | }, 7 | large: { 8 | margin: "24px auto", 9 | width: 200, 10 | height: 200, 11 | }, 12 | title: { 13 | marginBottom: 12, 14 | textAlign: "center", 15 | }, 16 | })); 17 | -------------------------------------------------------------------------------- /src/components/AppBar/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | root: { 5 | flexGrow: 1 6 | }, 7 | list: { 8 | width: 250 9 | }, 10 | menuButton: { 11 | marginRight: theme.spacing(2) 12 | }, 13 | title: { 14 | flexGrow: 1, 15 | color: "#ffffff", 16 | textDecoration: "none", 17 | "&:hover": { 18 | textDecoration: "underline" 19 | } 20 | } 21 | })); 22 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/Notifications/NotificationsList/NotificationItem/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | inline: { 5 | display: "inline", 6 | }, 7 | item: { 8 | "&:hover": { 9 | backgroundColor: "#f0f0f0", 10 | }, 11 | }, 12 | avatar: { 13 | width: 50, 14 | height: 50, 15 | marginTop: 6, 16 | marginRight: 12, 17 | }, 18 | })); 19 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Books/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | root: { 5 | display: "flex", 6 | flexWrap: "wrap", 7 | justifyContent: "space-around", 8 | overflow: "hidden", 9 | backgroundColor: theme.palette.background.paper 10 | }, 11 | gridList: { 12 | width: 500, 13 | height: 450 14 | }, 15 | icon: { 16 | color: "rgba(255, 255, 255, 0.54)" 17 | } 18 | })); 19 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/LikesPopup/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | padding: 16, 6 | minWidth: 400 7 | }, 8 | paper: { 9 | maxWidth: 400, 10 | margin: `${theme.spacing(1)}px auto`, 11 | padding: theme.spacing(2) 12 | }, 13 | title: { 14 | display: "flex", 15 | alignItems: "center" 16 | }, 17 | avatar: { 18 | background: "#f50057" 19 | } 20 | })); 21 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/hooks/useGoBack.js: -------------------------------------------------------------------------------- 1 | import { useDispatch } from "react-redux"; 2 | import { useHistory } from "react-router-dom"; 3 | import { useCallback } from "react"; 4 | 5 | import * as historyActions from "~/actions/history"; 6 | 7 | export default () => { 8 | const history = useHistory(); 9 | const dispatch = useDispatch(); 10 | 11 | return useCallback(() => { 12 | history.goBack(); 13 | dispatch(historyActions.goBack()); 14 | }, [history, dispatch]); 15 | }; 16 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/LikesPopup/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | padding: 16, 6 | minWidth: 400 7 | }, 8 | paper: { 9 | maxWidth: 400, 10 | margin: `${theme.spacing(1)}px auto`, 11 | padding: theme.spacing(2) 12 | }, 13 | title: { 14 | display: "flex", 15 | alignItems: "center" 16 | }, 17 | avatar: { 18 | background: "#f50057" 19 | } 20 | })); 21 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/Notifications/NotificationsList/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles((theme) => ({ 4 | container: { 5 | display: "flex", 6 | position: "relative", 7 | }, 8 | root: { 9 | backgroundColor: theme.palette.background.paper, 10 | flexShrink: 0, 11 | margin: "0 auto", 12 | }, 13 | close: { 14 | position: "absolute", 15 | top: 16, 16 | right: 16, 17 | }, 18 | })); 19 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/ClosePopupPage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | display: "grid", 6 | gridTemplateColumns: "350px 200px", 7 | alignItems: "center", 8 | padding: 20 9 | }, 10 | dialog: { 11 | width: 400, 12 | height: 600, 13 | display: "flex", 14 | flexDirection: "column", 15 | justifyContent: "center", 16 | alignItems: "center", 17 | fontSize: 24 18 | } 19 | })); 20 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/State/hooks/useClosePopup.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useRouteMatch, useLocation, useHistory } from "react-router-dom"; 3 | 4 | export default () => { 5 | const { state } = useLocation(); 6 | const history = useHistory(); 7 | const match = useRouteMatch(); 8 | 9 | return useCallback(() => { 10 | if (state && state.hasPrevRoute) { 11 | history.goBack(); 12 | } else { 13 | history.replace(match.url); 14 | } 15 | }, [state, history, match.url]); 16 | }; 17 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Posts/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16 6 | }, 7 | button: { 8 | marginRight: 16 9 | }, 10 | posts: { 11 | margin: "16px 0 16px 0", 12 | display: "flex", 13 | flexDirection: "row", 14 | flexWrap: "wrap" 15 | }, 16 | card: { 17 | maxWidth: 345, 18 | margin: "0 16px 16px 0", 19 | textDecoration: "none" 20 | }, 21 | media: { 22 | height: 140 23 | } 24 | })); 25 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/hooks/usePush.js: -------------------------------------------------------------------------------- 1 | import { useDispatch } from "react-redux"; 2 | import { useHistory } from "react-router-dom"; 3 | import { useCallback } from "react"; 4 | 5 | import * as historyActions from "~/actions/history"; 6 | 7 | export default () => { 8 | const history = useHistory(); 9 | const dispatch = useDispatch(); 10 | 11 | return useCallback( 12 | link => { 13 | history.push(link); 14 | dispatch(historyActions.push(link)); 15 | }, 16 | [history, dispatch] 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Desktop/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import List from "../List"; 4 | import Sidebar from "../Sidebar"; 5 | import useStyles from "./styles"; 6 | 7 | const Desktop = () => { 8 | const styles = useStyles(); 9 | 10 | return ( 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default Desktop; 23 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Posts/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: 16, 6 | }, 7 | button: { 8 | marginRight: 16, 9 | }, 10 | posts: { 11 | margin: "16px 0 16px 0", 12 | display: "flex", 13 | flexDirection: "row", 14 | flexWrap: "wrap", 15 | }, 16 | card: { 17 | maxWidth: 345, 18 | margin: "0 16px 16px 0", 19 | textDecoration: "none", 20 | }, 21 | media: { 22 | height: 200, 23 | }, 24 | })); 25 | -------------------------------------------------------------------------------- /src/services/calculateDeviceInfo.js: -------------------------------------------------------------------------------- 1 | export default width => { 2 | if (width <= 768) { 3 | return { 4 | isMobile: true, 5 | isTablet: false, 6 | isDesktop: false, 7 | device: "mobile" 8 | }; 9 | } 10 | 11 | if (width <= 1024) { 12 | return { 13 | isMobile: false, 14 | isTablet: true, 15 | isDesktop: false, 16 | device: "tablet" 17 | }; 18 | } 19 | 20 | return { 21 | isMobile: false, 22 | isTablet: false, 23 | isDesktop: true, 24 | device: "desktop" 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/hooks/useReplace.js: -------------------------------------------------------------------------------- 1 | import { useDispatch } from "react-redux"; 2 | import { useHistory } from "react-router-dom"; 3 | import { useCallback } from "react"; 4 | 5 | import * as historyActions from "~/actions/history"; 6 | 7 | export default () => { 8 | const history = useHistory(); 9 | const dispatch = useDispatch(); 10 | 11 | return useCallback( 12 | link => { 13 | history.replace(link); 14 | dispatch(historyActions.replace(link)); 15 | }, 16 | [history, dispatch] 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/Notifications/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | 4 | import Dialog from "@material-ui/core/Dialog"; 5 | 6 | import NotificationsList from "./NotificationsList"; 7 | 8 | const Notifications = ({ isOpened }) => { 9 | const history = useHistory(); 10 | 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default Notifications; 19 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/SignUpButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | 6 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 7 | 8 | const SignUpButton = () => { 9 | const signUpLink = usePrepareLink({ 10 | isRelativePath: true, 11 | to: "/sign-up" 12 | }); 13 | 14 | return ( 15 | 18 | ); 19 | }; 20 | 21 | export default SignUpButton; 22 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/AuthStatus/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | import useStyles from "./styles"; 5 | 6 | const AuthStatus = ({ isAuthorized }) => { 7 | const styles = useStyles(); 8 | 9 | return isAuthorized ? ( 10 |

You are authorized

11 | ) : ( 12 |

You are not authorized

13 | ); 14 | }; 15 | 16 | AuthStatus.propTypes = { 17 | isAuthorized: PropTypes.bool.isRequired 18 | }; 19 | 20 | export default AuthStatus; 21 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/SignUpForm/usePreventReload.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useCallback } from "react"; 2 | 3 | export default isPrenvent => { 4 | const preventReload = useCallback(event => { 5 | event.preventDefault(); 6 | event.returnValue = ""; 7 | }, []); 8 | 9 | useEffect(() => { 10 | if (isPrenvent) { 11 | window.addEventListener("beforeunload", preventReload); 12 | } 13 | return () => { 14 | if (isPrenvent) { 15 | window.removeEventListener("beforeunload", preventReload); 16 | } 17 | }; 18 | }, [isPrenvent, preventReload]); 19 | }; 20 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Year/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "@material-ui/core/Slider"; 3 | 4 | import useStyles from "./styles"; 5 | 6 | const Year = () => { 7 | const styles = useStyles(); 8 | 9 | return ( 10 |
11 | `${value} Year`} 14 | valueLabelDisplay="on" 15 | step={1} 16 | marks 17 | min={1990} 18 | max={2020} 19 | /> 20 |
21 | ); 22 | }; 23 | 24 | export default Year; 25 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/hooks/useClosePopup.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useRouteMatch, useHistory } from "react-router-dom"; 3 | 4 | import getHasPrevRoute from "../services/getHasPrevRoute"; 5 | 6 | export default () => { 7 | const history = useHistory(); 8 | const match = useRouteMatch(); 9 | 10 | const hasPrevRoute = getHasPrevRoute(); 11 | 12 | return useCallback(() => { 13 | if (hasPrevRoute) { 14 | history.goBack(); 15 | } else { 16 | history.replace(match.url); 17 | } 18 | }, [history, match.url, hasPrevRoute]); 19 | }; 20 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Miles/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "@material-ui/core/Slider"; 3 | 4 | import useStyles from "./styles"; 5 | 6 | const Miles = () => { 7 | const styles = useStyles(); 8 | 9 | return ( 10 |
11 | `${value} miles`} 14 | valueLabelDisplay="on" 15 | step={10} 16 | marks 17 | min={0} 18 | max={1000} 19 | /> 20 |
21 | ); 22 | }; 23 | 24 | export default Miles; 25 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Food/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | root: { 5 | maxWidth: 345 6 | }, 7 | media: { 8 | height: 0, 9 | paddingTop: "56.25%" // 16:9 10 | }, 11 | expand: { 12 | transform: "rotate(0deg)", 13 | marginLeft: "auto", 14 | transition: theme.transitions.create("transform", { 15 | duration: theme.transitions.duration.shortest 16 | }) 17 | }, 18 | expandOpen: { 19 | transform: "rotate(180deg)" 20 | }, 21 | avatar: { 22 | backgroundColor: "red" 23 | } 24 | })); 25 | -------------------------------------------------------------------------------- /src/components/App/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider } from "react-redux"; 3 | 4 | import createStore from "~/store"; 5 | 6 | import Router from "../Router"; 7 | import DeviceInfoHandler from "../DeviceInfoHandler"; 8 | import useStyles from "./styles"; 9 | 10 | const { store } = createStore(); 11 | function App() { 12 | const styles = useStyles(); 13 | 14 | return ( 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /src/reducer/history.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from "redux-actions"; 2 | 3 | import * as historyActions from "~/actions/history"; 4 | 5 | const defaultState = []; 6 | 7 | export default handleActions( 8 | { 9 | [historyActions.push]: (state, action) => { 10 | return [...state, action.payload]; 11 | }, 12 | [historyActions.replace]: (state, action) => { 13 | if (state.length === 0) { 14 | return state; 15 | } 16 | 17 | return [...state.slice(0, -1), action.payload]; 18 | }, 19 | [historyActions.goBack]: state => { 20 | return state.slice(0, -1); 21 | } 22 | }, 23 | defaultState 24 | ); 25 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/HistoryLogger/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | 4 | import updateSessionHistory from "../services/updateSessionHistory"; 5 | import clearCustomHistory from "../services/clearCustomHistory"; 6 | 7 | const HistoryLogger = () => { 8 | const history = useHistory(); 9 | 10 | useEffect(() => { 11 | const unlistenHistory = history.listen(updateSessionHistory); 12 | 13 | return () => { 14 | unlistenHistory(); 15 | clearCustomHistory(); 16 | }; 17 | }, [history]); 18 | 19 | return null; 20 | }; 21 | 22 | export default HistoryLogger; 23 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/services/updateSessionHistory.js: -------------------------------------------------------------------------------- 1 | import setCustomHistory from "./setCustomHistory"; 2 | import getCustomHistory from "./getCustomHistory"; 3 | 4 | export default (location, type) => { 5 | const customHistory = getCustomHistory(); 6 | 7 | switch (type) { 8 | case "REPLACE": { 9 | customHistory.pop(); 10 | customHistory.push(location); 11 | break; 12 | } 13 | case "PUSH": { 14 | customHistory.push(location); 15 | break; 16 | } 17 | case "POP": { 18 | customHistory.pop(); 19 | break; 20 | } 21 | default: { 22 | } 23 | } 24 | 25 | setCustomHistory(customHistory); 26 | }; 27 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Body from "./Body"; 4 | import Enging from "./Enging"; 5 | import Make from "./Make"; 6 | import Model from "./Model"; 7 | import Miles from "./Miles"; 8 | import Year from "./Year"; 9 | import useStyles from "./styles"; 10 | 11 | const TypeComponent = { 12 | body: Body, 13 | enging: Enging, 14 | make: Make, 15 | model: Model, 16 | miles: Miles, 17 | year: Year 18 | }; 19 | 20 | const Filter = ({ type }) => { 21 | const styles = useStyles(); 22 | 23 | const Component = TypeComponent[type]; 24 | 25 | return
{}
; 26 | }; 27 | 28 | export default Filter; 29 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/TwoPopupsPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Switch, Route, useRouteMatch, Redirect } from "react-router-dom"; 3 | 4 | import Posts from "../Posts"; 5 | import Post from "../Post"; 6 | import GetParameterPopups from "../GetParameterPopups"; 7 | 8 | const TwoPopupsPage = () => { 9 | const { url } = useRouteMatch(); 10 | 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | 27 | export default TwoPopupsPage; 28 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/List/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Vehicles from "../../data/vehicles.json"; 4 | import useStyles from "./styles"; 5 | 6 | const List = () => { 7 | const styles = useStyles(); 8 | 9 | return Vehicles.map(vehicle => { 10 | return ( 11 |
12 |
    13 |
  • make: {vehicle.make}
  • 14 |
  • model: {vehicle.model}
  • 15 |
  • year: {vehicle.year}
  • 16 |
  • engine: {vehicle.engine}
  • 17 |
  • miles: {vehicle.miles}
  • 18 |
  • bodyStyle: {vehicle.bodyStyle}
  • 19 |
20 |
21 | ); 22 | }); 23 | }; 24 | 25 | export default List; 26 | -------------------------------------------------------------------------------- /config/pnpTs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { resolveModuleName } = require('ts-pnp'); 4 | 5 | exports.resolveModuleName = ( 6 | typescript, 7 | moduleName, 8 | containingFile, 9 | compilerOptions, 10 | resolutionHost 11 | ) => { 12 | return resolveModuleName( 13 | moduleName, 14 | containingFile, 15 | compilerOptions, 16 | resolutionHost, 17 | typescript.resolveModuleName 18 | ); 19 | }; 20 | 21 | exports.resolveTypeReferenceDirective = ( 22 | typescript, 23 | moduleName, 24 | containingFile, 25 | compilerOptions, 26 | resolutionHost 27 | ) => { 28 | return resolveModuleName( 29 | moduleName, 30 | containingFile, 31 | compilerOptions, 32 | resolutionHost, 33 | typescript.resolveTypeReferenceDirective 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/PopupsRoutesPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Switch, Route, useRouteMatch, Redirect } from "react-router-dom"; 3 | 4 | import Posts from "../Posts"; 5 | import Post from "../Post"; 6 | import GetParameterPopups from "../GetParameterPopups"; 7 | 8 | const ResponsiveRoutesPage = () => { 9 | const { url } = useRouteMatch(); 10 | 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | 27 | export default ResponsiveRoutesPage; 28 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveFilter/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams, useHistory } from "react-router-dom"; 3 | import Button from "@material-ui/core/Button"; 4 | 5 | import Filter from "../Filter"; 6 | import useStyles from "./styles"; 7 | 8 | const ResponsiveFilter = () => { 9 | const { type } = useParams(); 10 | const styles = useStyles(); 11 | const history = useHistory(); 12 | 13 | return ( 14 |
15 | 23 | 24 |
25 | ); 26 | }; 27 | 28 | export default ResponsiveFilter; 29 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveList/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link, useRouteMatch } from "react-router-dom"; 3 | import Button from "@material-ui/core/Button"; 4 | 5 | import List from "../List"; 6 | import useStyles from "./styles"; 7 | 8 | const ResponsiveList = () => { 9 | const styles = useStyles(); 10 | const { url } = useRouteMatch(); 11 | 12 | return ( 13 |
14 | 23 | 24 |
25 | ); 26 | }; 27 | 28 | export default ResponsiveList; 29 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/AuthRoute/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Redirect } from "react-router-dom"; 3 | 4 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 5 | 6 | const AuthRoute = ({ children, isAuthorized, ...rest }) => { 7 | const loginLink = usePrepareLink({ 8 | to: "/login", 9 | isRelativePath: true 10 | }); 11 | 12 | return ( 13 | 16 | isAuthorized ? ( 17 | children 18 | ) : ( 19 | 25 | ) 26 | } 27 | /> 28 | ); 29 | }; 30 | 31 | export default AuthRoute; 32 | -------------------------------------------------------------------------------- /src/assets/icons/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/SignUpForm/useFormState.js: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | 3 | export default () => { 4 | const [fields, setFields] = useState({}); 5 | 6 | const onChange = useCallback( 7 | event => { 8 | const filteredFields = Object.entries({ 9 | ...fields, 10 | [event.target.name]: event.target.value 11 | }).reduce((memo, [key, value]) => { 12 | if (value) { 13 | memo[key] = value; 14 | } 15 | 16 | return memo; 17 | }, {}); 18 | 19 | setFields(filteredFields); 20 | }, 21 | [fields] 22 | ); 23 | 24 | const onSubmit = useCallback(event => { 25 | event.preventDefault(); 26 | }, []); 27 | 28 | return { 29 | fields, 30 | onChange, 31 | onSubmit 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/hooks/useClosePopup.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { useRouteMatch } from "react-router-dom"; 4 | 5 | import * as historySelectors from "../selectors/history"; 6 | 7 | import useGoBack from "./useGoBack"; 8 | import useReplace from "./useReplace"; 9 | 10 | export default () => { 11 | const hasPrevRoute = useSelector(state => { 12 | return historySelectors.getHasPrevRoute(state); 13 | }); 14 | 15 | const match = useRouteMatch(); 16 | const goBack = useGoBack(); 17 | const replace = useReplace(); 18 | 19 | return useCallback(() => { 20 | if (hasPrevRoute) { 21 | goBack(); 22 | } else { 23 | replace(match.url); 24 | } 25 | }, [goBack, replace, match.url, hasPrevRoute]); 26 | }; 27 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { GET_ENUMS } from "~/const/router"; 4 | 5 | import useGetPopupState from "./hooks/useGetPopupState"; 6 | import SignIn from "./Popups/SignIn"; 7 | import SignUp from "./Popups/SignUp"; 8 | import Notifications from "./Popups/Notifications"; 9 | 10 | const popups = { 11 | [GET_ENUMS.popup.signIn]: SignIn, 12 | [GET_ENUMS.popup.signUp]: SignUp, 13 | [GET_ENUMS.popup.notifications]: Notifications 14 | }; 15 | 16 | const GetParameterPopups = () => { 17 | const { mountedPopup, isOpened } = useGetPopupState(); 18 | const Component = popups[mountedPopup]; 19 | 20 | if (!Component) { 21 | return null; 22 | } 23 | 24 | return ; 25 | }; 26 | 27 | export default GetParameterPopups; 28 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/ClosePopupPage/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import GoBack from "../GoBack"; 4 | import Push from "../Push"; 5 | import CustomSessionHistory from "../CustomSessionHistory"; 6 | import CustomReduxHistory from "../CustomReduxHistory"; 7 | import State from "../State"; 8 | import useStyles from "./styles"; 9 | 10 | const ClosePopupPage = () => { 11 | const styles = useStyles(); 12 | 13 | return ( 14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 | ); 22 | }; 23 | 24 | export default ClosePopupPage; 25 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Make/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import FormControl from "@material-ui/core/FormControl"; 3 | import FormGroup from "@material-ui/core/FormGroup"; 4 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 5 | import Checkbox from "@material-ui/core/Checkbox"; 6 | 7 | const Make = () => { 8 | return ( 9 | 10 | 11 | } label="Ford" /> 12 | } 14 | label="Toyota" 15 | /> 16 | } 18 | label="Nissan" 19 | /> 20 | } label="Honda" /> 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default Make; 27 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/SimilarPicturesPopup/OriginalImage/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useRouteMatch, useHistory } from "react-router-dom"; 4 | 5 | import useStyles from "./styles"; 6 | 7 | const OriginalImage = ({ pictures }) => { 8 | const styles = useStyles(); 9 | const match = useRouteMatch(); 10 | const history = useHistory(); 11 | const [id] = useState(match && Number(match.params.id)); 12 | 13 | const picture = pictures.find((picture) => picture.id === id); 14 | 15 | if (!picture) { 16 | return null; 17 | } 18 | 19 | return ( 20 | 23 | ); 24 | }; 25 | 26 | OriginalImage.propTypes = { 27 | pictures: PropTypes.array.isRequired, 28 | }; 29 | 30 | export default OriginalImage; 31 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/data/books.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Code Complete: A Practical Handbook of Software Construction", 4 | "img": "https://spzone-simpleprogrammer.netdna-ssl.com/wp-content/uploads/2015/03/codecomplete.jpg", 5 | "author": "Steve McConnell" 6 | }, 7 | { 8 | "title": "Clean Code: A Handbook of Agile Software Craftsmanship", 9 | "img": "https://spzone-simpleprogrammer.netdna-ssl.com/wp-content/uploads/2018/12/Clean-Code.png", 10 | "author": "Robert C. Martin" 11 | }, 12 | { 13 | "title": "Design Patterns: Elements of Reusable Object-Oriented Software", 14 | "img": "https://images-na.ssl-images-amazon.com/images/I/51szD9HC9pL._SX395_BO1,204,203,200_.jpg", 15 | "author": "Erich Gamma" 16 | }, 17 | { 18 | "title": "Head First Design Patterns: A Brain-Friendly Guide", 19 | "img": "https://images-na.ssl-images-amazon.com/images/I/61ZG-hATOeL._SX430_BO1,204,203,200_.jpg", 20 | "author": "Elisabeth Robson" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/data/notifications.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "avatar": "", 5 | "title": "Brunch this weekend?", 6 | "user": "Ali Connors", 7 | "shortText": "I'll be in your neighborhood doing errands in this…", 8 | "fullText": "I'll be in your neighborhood doing errands in this weekend and one more thing what i want to say" 9 | }, 10 | { 11 | "id": 2, 12 | "avatar": "", 13 | "title": "Summer BBQ", 14 | "user": "to Scott, Alex, Jennifer", 15 | "shortText": "Wish I could come, but I'm out of town this…", 16 | "fullText": "Wish I could come, but I'm out of town this weekend but i need to ask you something important" 17 | }, 18 | { 19 | "id": 3, 20 | "avatar": "", 21 | "title": "Oui Oui", 22 | "user": "Sandra Adams", 23 | "shortText": "Do you have Paris recommendations? Have you ever…", 24 | "fullText": "Do you have Paris recommendations? Have you ever been in Belarus? or it's not important for you today?" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/hooks/useGetPopupState.js: -------------------------------------------------------------------------------- 1 | /* global clearTimeout */ 2 | import { useState, useEffect, useMemo } from "react"; 3 | 4 | import { GET_PARAMS } from "~/const/router"; 5 | import useGetParameter from "~/hooks/router/useGetParameter"; 6 | 7 | let timeout; 8 | 9 | export default () => { 10 | const popupName = useGetParameter(GET_PARAMS.popup); 11 | const [mountedPopup, setMountedPopup] = useState(popupName); 12 | 13 | useEffect(() => { 14 | if (popupName) { 15 | timeout && clearTimeout(timeout); 16 | setMountedPopup(popupName); 17 | } else { 18 | timeout = setTimeout(() => { 19 | setMountedPopup(null); 20 | }, 300); 21 | } 22 | }, [popupName]); 23 | 24 | useEffect(() => { 25 | return () => { 26 | timeout && clearTimeout(timeout); 27 | }; 28 | }, []); 29 | 30 | const isOpened = useMemo(() => Boolean(popupName), [popupName]); 31 | 32 | return { 33 | mountedPopup, 34 | isOpened 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationsList/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import List from "@material-ui/core/List"; 3 | import Divider from "@material-ui/core/Divider"; 4 | 5 | import notifications from "../../../../../data/notifications"; 6 | import NotificationItem from "./NotificationItem"; 7 | import useStyles from "./styles"; 8 | 9 | const NotificationsList = () => { 10 | const styles = useStyles(); 11 | 12 | return ( 13 | 14 | {notifications.map((notification, index, notifications) => { 15 | const isLastNotification = notifications.length - 1 !== index; 16 | 17 | return ( 18 | 19 | 20 | {isLastNotification && } 21 | 22 | ); 23 | })} 24 | 25 | ); 26 | }; 27 | 28 | export default NotificationsList; 29 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/data/gallery.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "src": "https://i.ytimg.com/vi/pLqipJNItIo/hqdefault.jpg?sqp=-oaymwEYCNIBEHZIVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLBkklsyaw9FxDmMKapyBYCn9tbPNQ", 4 | "title": "Don Diablo @ Tomorrowland Main Stage 2019 | Official…", 5 | "channel": "Don Diablo", 6 | "views": "396 k views", 7 | "createdAt": "a week ago" 8 | }, 9 | { 10 | "src": "https://i.ytimg.com/vi/_Uu12zY01ts/hqdefault.jpg?sqp=-oaymwEZCPYBEIoBSFXyq4qpAwsIARUAAIhCGAFwAQ==&rs=AOn4CLCpX6Jan2rxrCAZxJYDXppTP4MoQA", 11 | "title": "Queen - Greatest Hits", 12 | "channel": "Queen Official", 13 | "views": "40 M views", 14 | "createdAt": "3 years ago" 15 | }, 16 | { 17 | "src": "https://i.ytimg.com/vi/kkLk2XWMBf8/hqdefault.jpg?sqp=-oaymwEYCNIBEHZIVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLB4GZTFu1Ju2EPPPXnhMZtFVvYBaw", 18 | "title": "Calvin Harris, Sam Smith - Promises (Official Video)", 19 | "channel": "Calvin Harris", 20 | "views": "130 M views", 21 | "createdAt": "10 months ago" 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Model/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import FormControl from "@material-ui/core/FormControl"; 3 | import FormGroup from "@material-ui/core/FormGroup"; 4 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 5 | import Checkbox from "@material-ui/core/Checkbox"; 6 | 7 | const Model = () => { 8 | return ( 9 | 10 | 11 | } 13 | label="Fusion S" 14 | /> 15 | } 17 | label="Focus SE" 18 | /> 19 | } 21 | label="Fiesta SE" 22 | /> 23 | } 25 | label="Taurus SE" 26 | /> 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default Model; 33 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/SignIn/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import TextField from "@material-ui/core/TextField"; 5 | import Button from "@material-ui/core/Button"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const SignIn = ({ isOpened }) => { 10 | const styles = useStyles(); 11 | const history = useHistory(); 12 | 13 | return ( 14 | 15 |
16 |

Sign In

17 | 18 | 19 | 27 | 28 |
29 | ); 30 | }; 31 | 32 | export default SignIn; 33 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/SignIn/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import TextField from "@material-ui/core/TextField"; 5 | import Button from "@material-ui/core/Button"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const SignIn = ({ isOpened }) => { 10 | const styles = useStyles(); 11 | const history = useHistory(); 12 | 13 | return ( 14 | 15 |
16 |

Sign In

17 | 18 | 19 | 27 | 28 |
29 | ); 30 | }; 31 | 32 | export default SignIn; 33 | -------------------------------------------------------------------------------- /src/components/DeviceInfoHandler/index.jsx: -------------------------------------------------------------------------------- 1 | /* global window */ 2 | import React, { useState, useCallback, useEffect } from "react"; 3 | 4 | import DeviceInfoContext from "~/contexts/DeviceInfoContext"; 5 | import calculateDeviceInfo from "~/services/calculateDeviceInfo"; 6 | 7 | const DeviceInfoHandler = ({ children }) => { 8 | const [deviceInfo, setDeviceInfo] = useState( 9 | calculateDeviceInfo(window.innerWidth) 10 | ); 11 | 12 | const onResize = useCallback(() => { 13 | const newDeviceInfo = calculateDeviceInfo(window.innerWidth); 14 | 15 | if (deviceInfo.device !== newDeviceInfo.device) { 16 | setDeviceInfo(newDeviceInfo); 17 | } 18 | }, [deviceInfo]); 19 | 20 | useEffect(() => { 21 | window.addEventListener("resize", onResize); 22 | return () => { 23 | window.removeEventListener("resize", onResize); 24 | }; 25 | }, [deviceInfo, onResize]); 26 | 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default DeviceInfoHandler; 35 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/NotificationDetails/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | 5 | import { GET_PARAMS } from "~/const/router"; 6 | import useGetParameter from "~/hooks/router/useGetParameter"; 7 | 8 | import Details from "./Details"; 9 | 10 | const NotificationDetails = ({ isOpened }) => { 11 | const history = useHistory(); 12 | const queryNotificationId = useGetParameter(GET_PARAMS.notificationId); 13 | const [notificationId, setNotificationId] = useState( 14 | Number(queryNotificationId) 15 | ); 16 | 17 | useEffect(() => { 18 | if (queryNotificationId) { 19 | setNotificationId(Number(queryNotificationId)); 20 | } 21 | }, [queryNotificationId]); 22 | 23 | return ( 24 | 28 |
29 |
30 | ); 31 | }; 32 | 33 | export default NotificationDetails; 34 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { GET_ENUMS } from "~/const/router"; 4 | 5 | import useGetPopupsState from "./hooks/useGetPopupsState"; 6 | import SignIn from "./Popups/SignIn"; 7 | import SignUp from "./Popups/SignUp"; 8 | import Notifications from "./Popups/Notifications"; 9 | import NotificationDetails from "./Popups/NotificationDetails"; 10 | 11 | const mappedPopups = { 12 | [GET_ENUMS.popup.signIn]: SignIn, 13 | [GET_ENUMS.popup.signUp]: SignUp, 14 | [GET_ENUMS.popup.notifications]: Notifications, 15 | [GET_ENUMS.popup.notificationDetails]: NotificationDetails, 16 | }; 17 | 18 | const GetParameterPopups = () => { 19 | const { mountedPopups, popups } = useGetPopupsState(); 20 | 21 | return mountedPopups.map((mountedPopup) => { 22 | const Component = mappedPopups[mountedPopup]; 23 | 24 | if (!Component) { 25 | return null; 26 | } 27 | 28 | return ( 29 | 30 | ); 31 | }); 32 | }; 33 | 34 | export default GetParameterPopups; 35 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/ViewersPopup/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Paper from "@material-ui/core/Paper"; 3 | import Grid from "@material-ui/core/Grid"; 4 | import Avatar from "@material-ui/core/Avatar"; 5 | import Typography from "@material-ui/core/Typography"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const ViewersPopup = ({ viewers }) => { 10 | const styles = useStyles(); 11 | 12 | return ( 13 |
14 |

Viewers

15 | {viewers.map(like => { 16 | return ( 17 | 18 | 19 | 20 | {like.title.charAt(0)} 21 | 22 | 23 | {like.title} 24 | 25 | 26 | 27 | ); 28 | })} 29 |
30 | ); 31 | }; 32 | 33 | export default ViewersPopup; 34 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/ViewersPopup/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Paper from "@material-ui/core/Paper"; 3 | import Grid from "@material-ui/core/Grid"; 4 | import Avatar from "@material-ui/core/Avatar"; 5 | import Typography from "@material-ui/core/Typography"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const ViewersPopup = ({ viewers }) => { 10 | const styles = useStyles(); 11 | 12 | return ( 13 |
14 |

Viewers

15 | {viewers.map(like => { 16 | return ( 17 | 18 | 19 | 20 | {like.title.charAt(0)} 21 | 22 | 23 | {like.title} 24 | 25 | 26 | 27 | ); 28 | })} 29 |
30 | ); 31 | }; 32 | 33 | export default ViewersPopup; 34 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/SimilarPicturesPopup/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles((theme) => ({ 4 | container: { 5 | display: "flex", 6 | flexDirection: "column", 7 | padding: "16px 0", 8 | minWidth: 400, 9 | overflow: "hidden", 10 | }, 11 | title: { 12 | marginTop: 0, 13 | padding: "0 16px", 14 | display: "flex", 15 | flexDirection: "row", 16 | justifyContent: "space-between", 17 | }, 18 | grid: { 19 | flex: 1, 20 | display: "grid", 21 | "grid-template-columns": "220px 220px", 22 | "grid-gap": 10, 23 | overflow: "auto", 24 | padding: "0 16px", 25 | "&::-webkit-scrollbar-track": { 26 | "background-color": "#F5F5F5", 27 | }, 28 | "&::-webkit-scrollbar": { 29 | width: 4, 30 | "border-radius": 10, 31 | "background-color": "#F5F5F5", 32 | }, 33 | "&::-webkit-scrollbar-thumb": { 34 | "background-color": "#BCBCBC", 35 | }, 36 | }, 37 | button: { 38 | border: "none", 39 | padding: 0, 40 | cursor: "pointer", 41 | }, 42 | })); 43 | -------------------------------------------------------------------------------- /.github/workflows/master-deployment.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | name: Deploy to Amazon EC2 7 | 8 | jobs: 9 | deploy: 10 | name: Deploy 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Nodejs 12.14.0 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 12.14.0 21 | 22 | - uses: actions/cache@v1 23 | with: 24 | path: ~/.npm 25 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.os }}-node- 28 | 29 | - name: install and build 30 | run: | 31 | npm install 32 | npm run build 33 | env: 34 | CI: true 35 | 36 | - name: deploy 37 | uses: garygrossgarten/github-action-scp@release 38 | with: 39 | local: ./build 40 | remote: /var/www/sin9k 41 | host: ${{ secrets.SERVER_HOST }} 42 | username: ${{ secrets.SSH_USER }} 43 | privateKey: ${{ secrets.SERVER_SSH_KEY }} 44 | -------------------------------------------------------------------------------- /.github/workflows/develop-deployment.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - develop 5 | 6 | name: Deploy to stage Amazon EC2 7 | 8 | jobs: 9 | deploy: 10 | name: Deploy 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Nodejs 12.14.0 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 12.14.0 21 | 22 | - uses: actions/cache@v1 23 | with: 24 | path: ~/.npm 25 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.os }}-node- 28 | 29 | - name: install and build 30 | run: | 31 | npm install 32 | npm run build 33 | env: 34 | CI: true 35 | 36 | - name: deploy 37 | uses: garygrossgarten/github-action-scp@release 38 | with: 39 | local: ./build 40 | remote: /var/www/stage-sin9k 41 | host: ${{ secrets.SERVER_HOST }} 42 | username: ${{ secrets.SSH_USER }} 43 | privateKey: ${{ secrets.SERVER_SSH_KEY }} 44 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/LikesPopup/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Paper from "@material-ui/core/Paper"; 3 | import Grid from "@material-ui/core/Grid"; 4 | import Avatar from "@material-ui/core/Avatar"; 5 | import Typography from "@material-ui/core/Typography"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const LikesPopup = ({ likes }) => { 10 | const styles = useStyles(); 11 | 12 | return ( 13 |
14 |

Likes

15 | {likes.map(like => { 16 | return ( 17 | 18 | 19 | 20 | 21 | {like.title.charAt(0)} 22 | 23 | 24 | 25 | {like.title} 26 | 27 | 28 | 29 | ); 30 | })} 31 |
32 | ); 33 | }; 34 | 35 | export default LikesPopup; 36 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/GoBack/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Link, useHistory } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | import Dialog from "@material-ui/core/Dialog"; 6 | 7 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 8 | 9 | const GoBack = ({ dialogClassName }) => { 10 | const history = useHistory(); 11 | 12 | const goBackLink = usePrepareLink({ 13 | to: "/history-go-back", 14 | isRelativePath: true 15 | }); 16 | 17 | return ( 18 | <> 19 |

call history.goBack() by close -

20 |
21 | 24 | { 27 | return ( 28 | 29 |
history.goBack
30 |
31 | ); 32 | }} 33 | /> 34 |
35 | 36 | ); 37 | }; 38 | 39 | export default GoBack; 40 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/LikesPopup/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Paper from "@material-ui/core/Paper"; 3 | import Grid from "@material-ui/core/Grid"; 4 | import Avatar from "@material-ui/core/Avatar"; 5 | import Typography from "@material-ui/core/Typography"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const LikesPopup = ({ likes }) => { 10 | const styles = useStyles(); 11 | 12 | return ( 13 |
14 |

Likes

15 | {likes.map(like => { 16 | return ( 17 | 18 | 19 | 20 | 21 | {like.title.charAt(0)} 22 | 23 | 24 | 25 | {like.title} 26 | 27 | 28 | 29 | ); 30 | })} 31 |
32 | ); 33 | }; 34 | 35 | export default LikesPopup; 36 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Body/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import FormControl from "@material-ui/core/FormControl"; 3 | import FormGroup from "@material-ui/core/FormGroup"; 4 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 5 | import Checkbox from "@material-ui/core/Checkbox"; 6 | 7 | const Body = () => { 8 | return ( 9 | 10 | 11 | } 13 | label="Convertibles" 14 | /> 15 | } 17 | label="Coupes" 18 | /> 19 | } 21 | label="Hatchbacks" 22 | /> 23 | } 25 | label="Sedans" 26 | /> 27 | } 29 | label="Trucks" 30 | /> 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default Body; 37 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/hooks/useGetPopupsState.js: -------------------------------------------------------------------------------- 1 | /* global clearTimeout */ 2 | import { useState, useEffect, useMemo } from "react"; 3 | 4 | import { GET_PARAMS } from "~/const/router"; 5 | import useGetParameter from "~/hooks/router/useGetParameter"; 6 | 7 | let timeout; 8 | 9 | function parseStringifiedValue(value) { 10 | return value ? value.split(",") : []; 11 | } 12 | 13 | export default () => { 14 | const rawPopups = useGetParameter(GET_PARAMS.popup); 15 | const [mountedPopups, setMountedPopups] = useState( 16 | parseStringifiedValue(rawPopups) 17 | ); 18 | 19 | useEffect(() => { 20 | if (rawPopups) { 21 | timeout && clearTimeout(timeout); 22 | setMountedPopups(rawPopups.split(",")); 23 | } else { 24 | timeout = setTimeout(() => { 25 | setMountedPopups([]); 26 | }, 300); 27 | } 28 | }, [rawPopups]); 29 | 30 | useEffect(() => { 31 | return () => { 32 | timeout && clearTimeout(timeout); 33 | }; 34 | }, []); 35 | 36 | const popups = useMemo(() => parseStringifiedValue(rawPopups), [rawPopups]); 37 | 38 | return { 39 | mountedPopups, 40 | popups, 41 | }; 42 | }; 43 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/State/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link, Route } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | import Dialog from "@material-ui/core/Dialog"; 6 | 7 | import usePreparePopupLink from "./hooks/usePreparePopupLink"; 8 | import useClosePopup from "./hooks/useClosePopup"; 9 | 10 | const State = ({ dialogClassName }) => { 11 | const stateLink = usePreparePopupLink({ 12 | to: "/state", 13 | isRelativePath: true 14 | }); 15 | 16 | const closePopup = useClosePopup(); 17 | 18 | return ( 19 | <> 20 |

use state to detect goBack or replace by close -

21 |
22 | 25 | { 28 | return ( 29 | 30 |
State
31 |
32 | ); 33 | }} 34 | /> 35 |
36 | 37 | ); 38 | }; 39 | 40 | export default State; 41 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Gallery/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Grid from "@material-ui/core/Grid"; 4 | import Box from "@material-ui/core/Box"; 5 | import Typography from "@material-ui/core/Typography"; 6 | 7 | import gallery from "../../data/gallery"; 8 | 9 | const Gallery = () => { 10 | return ( 11 | 12 | {gallery.map((item, index) => ( 13 | 14 | {item.title} 19 | 20 | 21 | 22 | {item.title} 23 | 24 | 25 | {item.channel} 26 | 27 | 28 | {`${item.views} • ${item.createdAt}`} 29 | 30 | 31 | 32 | ))} 33 | 34 | ); 35 | }; 36 | 37 | export default Gallery; 38 | -------------------------------------------------------------------------------- /src/hooks/router/usePrepareLink.js: -------------------------------------------------------------------------------- 1 | import { useLocation, useRouteMatch } from "react-router-dom"; 2 | 3 | export default ({ 4 | to, 5 | isRelativePath = false, 6 | query = {}, 7 | pushToQuery = {}, 8 | hash, 9 | keepOldQuery = false, 10 | state = {}, 11 | }) => { 12 | const location = useLocation(); 13 | const match = useRouteMatch(); 14 | 15 | let pathname; 16 | 17 | if (match && isRelativePath) { 18 | pathname = match.url + to; 19 | } else { 20 | pathname = to || location.pathname; 21 | } 22 | 23 | const newQuery = keepOldQuery 24 | ? new URLSearchParams(location.search) 25 | : new URLSearchParams(); 26 | 27 | Object.entries(query).forEach(([key, value]) => { 28 | newQuery.set(key, value); 29 | }); 30 | 31 | Object.entries(pushToQuery).forEach(([key, value]) => { 32 | const currentValue = newQuery.get(key); 33 | const splittedValue = currentValue ? currentValue.split(",") : []; 34 | splittedValue.push(value); 35 | 36 | newQuery.set(key, splittedValue); 37 | }); 38 | 39 | return { 40 | pathname: pathname.replace(/\/\//g, "/"), 41 | search: newQuery.toString() ? `?${newQuery.toString()}` : "", 42 | hash, 43 | state, 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Filter/Enging/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import FormControl from "@material-ui/core/FormControl"; 3 | import FormGroup from "@material-ui/core/FormGroup"; 4 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 5 | import Checkbox from "@material-ui/core/Checkbox"; 6 | 7 | const Enging = () => { 8 | return ( 9 | 10 | 11 | } 13 | label="2 Cylinders" 14 | /> 15 | } 17 | label="3 Cylinders" 18 | /> 19 | } 21 | label="4 Cylinders" 22 | /> 23 | } 25 | label="5 Cylinders" 26 | /> 27 | } 29 | label="6 Cylinders" 30 | /> 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default Enging; 37 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveRoutesPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { useRouteMatch, Switch, Route, Redirect } from "react-router-dom"; 3 | 4 | import DeviceInfoContext from "~/contexts/DeviceInfoContext"; 5 | 6 | import Desktop from "../Desktop"; 7 | import ResponsiveList from "../ResponsiveList"; 8 | import ResponsiveFilters from "../ResponsiveFilters"; 9 | import ResponsiveFilter from "../ResponsiveFilter"; 10 | 11 | const ResponsiveRoutesPage = () => { 12 | const { url } = useRouteMatch(); 13 | 14 | const deviceInfo = useContext(DeviceInfoContext); 15 | return deviceInfo.isDesktop ? ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | ) : ( 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }; 37 | 38 | export default ResponsiveRoutesPage; 39 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/ThirdStep/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useHistory } from "react-router-dom"; 4 | 5 | import IconButton from "@material-ui/core/IconButton"; 6 | import KeyboardBackspace from "@material-ui/icons/KeyboardBackspace"; 7 | 8 | import getFormData from "../../services/getFormData"; 9 | import useStyles from "./styles"; 10 | 11 | const ThirdStep = ({ title }) => { 12 | const [initialValues] = useState(getFormData()); 13 | const history = useHistory(); 14 | const styles = useStyles(); 15 | 16 | const onBack = useCallback(() => { 17 | history.goBack(); 18 | }, [history]); 19 | 20 | return ( 21 |
22 |

{title}

23 | 24 | 25 | 26 | {Object.entries(initialValues).map(([label, value]) => { 27 | return ( 28 |

29 | {label}: {value} 30 |

31 | ); 32 | })} 33 |
34 | ); 35 | }; 36 | 37 | ThirdStep.propTypes = { 38 | title: PropTypes.string.isRequired, 39 | }; 40 | 41 | export default ThirdStep; 42 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/SignUp/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import TextField from "@material-ui/core/TextField"; 5 | import Button from "@material-ui/core/Button"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const SignUp = ({ isOpened }) => { 10 | const styles = useStyles(); 11 | const history = useHistory(); 12 | 13 | return ( 14 | 15 |
16 |

Sign Up

17 | 18 | 19 | 20 | 21 | 22 | 30 | 31 |
32 | ); 33 | }; 34 | 35 | export default SignUp; 36 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/SignUp/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import TextField from "@material-ui/core/TextField"; 5 | import Button from "@material-ui/core/Button"; 6 | 7 | import useStyles from "./styles"; 8 | 9 | const SignUp = ({ isOpened }) => { 10 | const styles = useStyles(); 11 | const history = useHistory(); 12 | 13 | return ( 14 | 15 |
16 |

Sign Up

17 | 18 | 19 | 20 | 21 | 22 | 30 | 31 |
32 | ); 33 | }; 34 | 35 | export default SignUp; 36 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/Push/index.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from "react"; 2 | import { useHistory, useRouteMatch, Link, Route } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | import Dialog from "@material-ui/core/Dialog"; 6 | 7 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 8 | 9 | const Push = ({ dialogClassName }) => { 10 | const match = useRouteMatch(); 11 | const history = useHistory(); 12 | 13 | const pushLink = usePrepareLink({ 14 | to: "/push", 15 | isRelativePath: true 16 | }); 17 | 18 | const goBack = useCallback(() => { 19 | history.push(match.url); 20 | }, [history, match.url]); 21 | 22 | return ( 23 | <> 24 |

call push() to previous route by close -

25 |
26 | 29 | { 32 | return ( 33 | 34 |
Push
35 |
36 | ); 37 | }} 38 | /> 39 |
40 | 41 | ); 42 | }; 43 | 44 | export default Push; 45 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | 4 | import Dialog from "@material-ui/core/Dialog"; 5 | 6 | import { GET_PARAMS } from "~/const/router"; 7 | import useGetParameter from "~/hooks/router/useGetParameter"; 8 | 9 | import NotificationsList from "./NotificationsList"; 10 | import NotificationDetails from "./NotificationDetails"; 11 | import useStyles from "./styles"; 12 | 13 | const Notifications = ({ isOpened }) => { 14 | const notificationId = useGetParameter(GET_PARAMS.notificationId); 15 | const history = useHistory(); 16 | const [id, setId] = useState(notificationId); 17 | const styles = useStyles(); 18 | 19 | useEffect(() => { 20 | if (notificationId) { 21 | setId(notificationId); 22 | } 23 | }, [notificationId]); 24 | 25 | return ( 26 | 33 |
34 | 35 | {id && } 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default Notifications; 42 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationDetails/index.jsx: -------------------------------------------------------------------------------- 1 | /*global parseInt */ 2 | import React from "react"; 3 | import Card from "@material-ui/core/Card"; 4 | import CardContent from "@material-ui/core/CardContent"; 5 | import Typography from "@material-ui/core/Typography"; 6 | import Avatar from "@material-ui/core/Avatar"; 7 | 8 | import notifications from "../../../../../data/notifications"; 9 | import useStyles from "./styles"; 10 | 11 | const NotificationDetails = ({ notificationId }) => { 12 | const styles = useStyles(); 13 | 14 | const notification = notifications.find( 15 | notification => parseInt(notificationId) === notification.id 16 | ); 17 | 18 | if (!notification) { 19 | return null; 20 | } 21 | 22 | return ( 23 | 24 | 25 | 30 | 31 | {notification.title} 32 | 33 | 34 | {notification.fullText} 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | export default NotificationDetails; 42 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/Notifications/NotificationsList/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import List from "@material-ui/core/List"; 4 | import Divider from "@material-ui/core/Divider"; 5 | import IconButton from "@material-ui/core/IconButton"; 6 | import CloseIcon from "@material-ui/icons/Close"; 7 | 8 | import notifications from "../../../../../data/notifications"; 9 | import NotificationItem from "./NotificationItem"; 10 | import useStyles from "./styles"; 11 | 12 | const NotificationsList = () => { 13 | const styles = useStyles(); 14 | const history = useHistory(); 15 | 16 | return ( 17 |
18 | 19 | 20 | 21 | 22 | {notifications.map((notification, index, notifications) => { 23 | const isLastNotification = notifications.length - 1 !== index; 24 | 25 | return ( 26 | 27 | 28 | {isLastNotification && } 29 | 30 | ); 31 | })} 32 | 33 |
34 | ); 35 | }; 36 | 37 | export default NotificationsList; 38 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const camelcase = require('camelcase'); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFilename = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFilename}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/CustomPrompt/index.jsx: -------------------------------------------------------------------------------- 1 | /* global JSON */ 2 | import React from "react"; 3 | 4 | import Dialog from "@material-ui/core/Dialog"; 5 | import Button from "@material-ui/core/Button"; 6 | import DialogActions from "@material-ui/core/DialogActions"; 7 | import DialogContent from "@material-ui/core/DialogContent"; 8 | import DialogContentText from "@material-ui/core/DialogContentText"; 9 | 10 | const CustomPrompt = ({ message, cleanUp }) => { 11 | const parsedMessage = JSON.parse(message); 12 | 13 | const cancel = () => cleanUp(false); 14 | const ok = () => cleanUp(true); 15 | 16 | return ( 17 | 18 | 19 | 20 | If you leave the page, the following data will be lost: 21 | 22 |
    23 | {Object.entries(parsedMessage.fields).map(([key, value]) => { 24 | return ( 25 |
  • 26 | {key}: {value} 27 |
  • 28 | ); 29 | })} 30 |
31 |
32 | 33 | 36 | 39 | 40 |
41 | ); 42 | }; 43 | 44 | export default CustomPrompt; 45 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomReduxHistory/index.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from "react"; 2 | import { Route } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | import Dialog from "@material-ui/core/Dialog"; 6 | 7 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 8 | 9 | import useClosePopup from "./hooks/useClosePopup"; 10 | import usePush from "./hooks/usePush"; 11 | 12 | const CustomReduxHistory = ({ dialogClassName }) => { 13 | const customReduxHistoryLink = usePrepareLink({ 14 | to: "/custom-redux-history", 15 | isRelativePath: true 16 | }); 17 | 18 | const closePopup = useClosePopup(); 19 | const push = usePush(); 20 | 21 | const openPopup = useCallback(() => { 22 | push(customReduxHistoryLink); 23 | }, [push, customReduxHistoryLink]); 24 | 25 | return ( 26 | <> 27 |

use custom history to detect goBack or replace by close -

28 |
29 | 32 | ( 35 | 36 |
Redux History
37 |
38 | )} 39 | /> 40 |
41 | 42 | ); 43 | }; 44 | 45 | export default CustomReduxHistory; 46 | -------------------------------------------------------------------------------- /src/pages/ClosePopup/components/CustomSessionHistory/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Link } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | import Dialog from "@material-ui/core/Dialog"; 6 | 7 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 8 | 9 | import useClosePopup from "./hooks/useClosePopup"; 10 | import HistoryLogger from "./HistoryLogger"; 11 | 12 | const CustomSessionHistory = ({ dialogClassName }) => { 13 | const customSessionHistoryLink = usePrepareLink({ 14 | to: "/custom-session-history", 15 | isRelativePath: true 16 | }); 17 | 18 | const closePopup = useClosePopup(); 19 | 20 | return ( 21 | <> 22 | 23 |

use custom history to detect goBack or replace by close -

24 |
25 | 32 | { 35 | return ( 36 | 37 |
Session History
38 |
39 | ); 40 | }} 41 | /> 42 |
43 | 44 | ); 45 | }; 46 | 47 | export default CustomSessionHistory; 48 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/NotificationDetails/Details/index.jsx: -------------------------------------------------------------------------------- 1 | /*global parseInt */ 2 | import React from "react"; 3 | import Card from "@material-ui/core/Card"; 4 | import CardContent from "@material-ui/core/CardContent"; 5 | import Typography from "@material-ui/core/Typography"; 6 | import Avatar from "@material-ui/core/Avatar"; 7 | 8 | import notifications from "../../../../../data/notifications"; 9 | import useStyles from "./styles"; 10 | 11 | const Details = ({ notificationId }) => { 12 | const styles = useStyles(); 13 | 14 | const notification = notifications.find( 15 | (notification) => parseInt(notificationId) === notification.id 16 | ); 17 | 18 | if (!notification) { 19 | return null; 20 | } 21 | 22 | return ( 23 | 24 | 25 | 30 | 31 | {notification.title} 32 | 33 | 34 | {notification.user} 35 | 36 | 37 | {notification.fullText} 38 | 39 | 40 | 41 | ); 42 | }; 43 | 44 | export default Details; 45 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/CustomPromptPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Switch, Route, useHistory } from "react-router-dom"; 3 | 4 | import Dialog from "@material-ui/core/Dialog"; 5 | 6 | import DeviceInfoContext from "~/contexts/DeviceInfoContext"; 7 | 8 | import SignUpButton from "../SignUpButton"; 9 | import SignUpForm from "../SignUpForm"; 10 | import useStyles from "./styles"; 11 | 12 | const CustomPromptPage = () => { 13 | const styles = useStyles(); 14 | const deviceInfo = useContext(DeviceInfoContext); 15 | const history = useHistory(); 16 | 17 | return ( 18 |
19 | {deviceInfo.isMobile ? ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) : ( 29 | <> 30 | 31 | 32 | 33 | { 36 | return ( 37 | 38 | 39 | 40 | ); 41 | }} 42 | /> 43 | 44 | )} 45 |
46 | ); 47 | }; 48 | 49 | export default CustomPromptPage; 50 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/StepperPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | import Stepper from "@material-ui/core/Stepper"; 5 | import Step from "@material-ui/core/Step"; 6 | import StepLabel from "@material-ui/core/StepLabel"; 7 | 8 | import clearFormData from "../../services/clearFormData"; 9 | import FirstStep from "../FirstStep"; 10 | import SecondStep from "../SecondStep"; 11 | import ThirdStep from "../ThirdStep"; 12 | import useStyles from "./styles"; 13 | 14 | const TABS = [ 15 | { 16 | component: FirstStep, 17 | title: "First Step", 18 | }, 19 | { 20 | component: SecondStep, 21 | title: "Second Step", 22 | }, 23 | { 24 | component: ThirdStep, 25 | title: "Review", 26 | }, 27 | ]; 28 | 29 | const StepperPage = () => { 30 | const { state = { activeStep: 0 } } = useLocation(); 31 | const styles = useStyles(); 32 | 33 | const tab = TABS[state.activeStep]; 34 | 35 | useEffect(() => { 36 | return () => { 37 | clearFormData(); 38 | }; 39 | }, []); 40 | 41 | return ( 42 |
43 |

Stepper

44 | 45 | {TABS.map(({ title }) => { 46 | return ( 47 | 48 | {title} 49 | 50 | ); 51 | })} 52 | 53 | {tab && } 54 |
55 | ); 56 | }; 57 | 58 | export default StepperPage; 59 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | 19 | const jest = require('jest'); 20 | const execSync = require('child_process').execSync; 21 | let argv = process.argv.slice(2); 22 | 23 | function isInGitRepository() { 24 | try { 25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); 26 | return true; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | function isInMercurialRepository() { 33 | try { 34 | execSync('hg --cwd . root', { stdio: 'ignore' }); 35 | return true; 36 | } catch (e) { 37 | return false; 38 | } 39 | } 40 | 41 | // Watch unless on CI or explicitly running all tests 42 | if ( 43 | !process.env.CI && 44 | argv.indexOf('--watchAll') === -1 && 45 | argv.indexOf('--watchAll=false') === -1 46 | ) { 47 | // https://github.com/facebook/create-react-app/issues/5210 48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository(); 49 | argv.push(hasSourceControl ? '--watch' : '--watchAll'); 50 | } 51 | 52 | 53 | jest.run(argv); 54 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Books/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import GridList from "@material-ui/core/GridList"; 4 | import GridListTile from "@material-ui/core/GridListTile"; 5 | import GridListTileBar from "@material-ui/core/GridListTileBar"; 6 | import ListSubheader from "@material-ui/core/ListSubheader"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | import InfoIcon from "@material-ui/icons/Info"; 9 | 10 | import books from "../../data/books"; 11 | import useStyles from "./styles"; 12 | 13 | const Books = () => { 14 | const styles = useStyles(); 15 | 16 | return ( 17 |
18 | 19 | 20 | December 21 | 22 | {books.map(book => ( 23 | 24 | {book.title} 25 | by: {book.author}} 28 | actionIcon={ 29 | 33 | 34 | 35 | } 36 | /> 37 | 38 | ))} 39 | 40 |
41 | ); 42 | }; 43 | 44 | export default Books; 45 | -------------------------------------------------------------------------------- /src/pages/Home/components/HomePage/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | container: { 5 | padding: "16px", 6 | }, 7 | card: { 8 | display: "flex", 9 | flexDirection: "row", 10 | marginBottom: 16, 11 | textDecoration: "none", 12 | maxWidth: 850, 13 | position: "relative", 14 | "&:hover": { 15 | backgroundColor: "#E8E8E8", 16 | }, 17 | "@media (max-width: 750px)": { 18 | maxWidth: 450, 19 | flexDirection: "column", 20 | margin: "0 auto 30px", 21 | }, 22 | }, 23 | cardLink: { 24 | position: "absolute", 25 | top: 0, 26 | right: 0, 27 | bottom: 0, 28 | left: 0, 29 | }, 30 | info: { 31 | overflow: "hidden", 32 | display: "flex", 33 | flexDirection: "column", 34 | padding: "6px 0", 35 | }, 36 | image: { 37 | flexShrink: 0, 38 | width: 300, 39 | marginRight: 16, 40 | "@media (max-width: 750px)": { 41 | width: "100%", 42 | }, 43 | }, 44 | title: { 45 | color: "#030303", 46 | fontSize: 22, 47 | marginTop: 0, 48 | marginBottom: 6, 49 | textOverflow: "ellipsis", 50 | overflow: "hidden", 51 | whiteSpace: "nowrap", 52 | }, 53 | description: { 54 | margin: 0, 55 | color: "#606060", 56 | overflow: "hidden", 57 | textOverflow: "ellipsis", 58 | display: "-webkit-box", 59 | "-webkit-line-clamp": 4, 60 | "-webkit-box-orient": "vertical", 61 | "@media (max-width: 750px)": { 62 | "-webkit-line-clamp": 6, 63 | }, 64 | }, 65 | social: { 66 | marginTop: "auto", 67 | marginBottom: 0, 68 | paddingTop: 12, 69 | }, 70 | link: { 71 | textDecoration: "none", 72 | marginRight: 12, 73 | }, 74 | })); 75 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/GetParameterPopups/Popups/Notifications/NotificationsList/NotificationItem/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import ListItem from "@material-ui/core/ListItem"; 5 | import ListItemText from "@material-ui/core/ListItemText"; 6 | import ListItemAvatar from "@material-ui/core/ListItemAvatar"; 7 | import Avatar from "@material-ui/core/Avatar"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 11 | import { GET_PARAMS } from "~/const/router"; 12 | 13 | import useStyles from "./styles"; 14 | 15 | const NotificationsItem = ({ notification }) => { 16 | const styles = useStyles(); 17 | 18 | const link = usePrepareLink({ 19 | query: { 20 | [GET_PARAMS.notificationId]: notification.id 21 | }, 22 | keepOldQuery: true 23 | }); 24 | 25 | return ( 26 | 33 | 34 | 35 | 36 | 40 | 46 | {notification.user} 47 | 48 | {notification.shortText} 49 | 50 | } 51 | /> 52 | 53 | ); 54 | }; 55 | 56 | export default NotificationsItem; 57 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/RestorePreventedRoutePage/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Switch, Route, Redirect } from "react-router-dom"; 3 | 4 | import AuthRoute from "../AuthRoute"; 5 | import AuthStatus from "../AuthStatus"; 6 | import Links from "../Links"; 7 | import Gallery from "../Gallery"; 8 | import Books from "../Books"; 9 | import Login from "../Login"; 10 | import Food from "../Food"; 11 | import useStyles from "./styles"; 12 | 13 | const RestorePreventedRoutePage = () => { 14 | const [isAuthorized, setIsAuthorized] = useState(false); 15 | const styles = useStyles(); 16 | 17 | return ( 18 |
19 | 20 | 21 | 22 | 23 |

HOME PAGE

24 |
25 | 26 | 27 | 28 | 33 | 34 | 35 | 40 | 41 | 42 | 43 | 47 | 48 | 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default RestorePreventedRoutePage; 55 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/ResponsiveFilters/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link, useRouteMatch, useHistory } from "react-router-dom"; 3 | import List from "@material-ui/core/List"; 4 | import ListItem from "@material-ui/core/ListItem"; 5 | import ListItemText from "@material-ui/core/ListItemText"; 6 | import Divider from "@material-ui/core/Divider"; 7 | import Button from "@material-ui/core/Button"; 8 | 9 | import useStyles from "./styles"; 10 | 11 | const ResponsiveFilters = () => { 12 | const styles = useStyles(); 13 | const { url } = useRouteMatch(); 14 | const history = useHistory(); 15 | 16 | return ( 17 | <> 18 | 26 |

Filters

27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | }; 55 | 56 | export default ResponsiveFilters; 57 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/GetParameterPopups/Popups/Notifications/NotificationsList/NotificationItem/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import ListItem from "@material-ui/core/ListItem"; 5 | import ListItemText from "@material-ui/core/ListItemText"; 6 | import ListItemAvatar from "@material-ui/core/ListItemAvatar"; 7 | import Avatar from "@material-ui/core/Avatar"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 11 | import { GET_PARAMS, GET_ENUMS } from "~/const/router"; 12 | 13 | import useStyles from "./styles"; 14 | 15 | const NotificationsItem = ({ notification }) => { 16 | const styles = useStyles(); 17 | 18 | const link = usePrepareLink({ 19 | query: { 20 | [GET_PARAMS.notificationId]: notification.id, 21 | }, 22 | pushToQuery: { 23 | [GET_PARAMS.popup]: GET_ENUMS.popup.notificationDetails, 24 | }, 25 | keepOldQuery: true, 26 | }); 27 | 28 | return ( 29 | 35 | 36 | 41 | 42 | 46 | 52 | {notification.user} 53 | 54 |
55 | {notification.shortText} 56 | 57 | } 58 | /> 59 |
60 | ); 61 | }; 62 | 63 | export default NotificationsItem; 64 | -------------------------------------------------------------------------------- /src/components/Router/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { BrowserRouter, Switch, Route } from "react-router-dom"; 4 | 5 | import Home from "~/pages/Home"; 6 | import ResponsiveRoutesPage from "~/pages/ResponsiveRoutes"; 7 | import PopupsRoutesPage from "~/pages/PopupsRoutes"; 8 | import CustomPromptPage from "~/pages/CustomPrompt"; 9 | import ClosePopupPage from "~/pages/ClosePopup"; 10 | import RestorePreventedRoutePage from "~/pages/RestorePreventedRoute"; 11 | import StepperPage from "~/pages/Stepper"; 12 | import TwoPopupsPage from "~/pages/TwoPopups"; 13 | 14 | import AppBar from "../AppBar"; 15 | import CustomPrompt from "../CustomPrompt"; 16 | 17 | const Router = () => { 18 | const userConfirmation = useCallback((message, callback) => { 19 | const node = document.getElementById("custom-prompt"); 20 | 21 | const cleanUp = (answer) => { 22 | ReactDOM.unmountComponentAtNode(node); 23 | callback(answer); 24 | }; 25 | 26 | ReactDOM.render(, node); 27 | }, []); 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | }; 61 | 62 | export default Router; 63 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Links/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 5 | 6 | import useStyles from "./styles"; 7 | 8 | const Links = () => { 9 | const styles = useStyles(); 10 | 11 | const homeLink = usePrepareLink({ 12 | to: "/", 13 | isRelativePath: true 14 | }); 15 | 16 | const galleryLink = usePrepareLink({ 17 | to: "/gallery", 18 | isRelativePath: true 19 | }); 20 | 21 | const booksLink = usePrepareLink({ 22 | to: "/books", 23 | isRelativePath: true 24 | }); 25 | 26 | const foodLink = usePrepareLink({ 27 | to: "/food", 28 | isRelativePath: true 29 | }); 30 | 31 | const loginLink = usePrepareLink({ 32 | to: "/login", 33 | isRelativePath: true 34 | }); 35 | 36 | return ( 37 |
    38 |
  • 39 | 45 | Home Page 46 | 47 |
  • 48 |
  • 49 | 54 | Gallery for everyone 55 | 56 |
  • 57 |
  • 58 | 63 | Books for authorized users 64 | 65 |
  • 66 |
  • 67 | 72 | Food for authorized users 73 | 74 |
  • 75 |
  • 76 | 81 | Login 82 | 83 |
  • 84 |
85 | ); 86 | }; 87 | 88 | export default Links; 89 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Login/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from "react"; 2 | import { useLocation, useHistory, Route, Switch } from "react-router-dom"; 3 | 4 | import MaterialSwitch from "@material-ui/core/Switch"; 5 | import FormControl from "@material-ui/core/FormControl"; 6 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 7 | import Button from "@material-ui/core/Button"; 8 | 9 | import useStyles from "./styles"; 10 | 11 | const Login = ({ isAuthorized, setIsAuthorized }) => { 12 | const { state } = useLocation(); 13 | const history = useHistory(); 14 | 15 | const [value, setValue] = useState(isAuthorized); 16 | 17 | const onChange = useCallback((event, value) => setValue(value), [setValue]); 18 | const onSignIn = useCallback(() => { 19 | setIsAuthorized(value); 20 | const link = (state && state.from) || "/restore-prevented-route"; 21 | history.replace(link); 22 | }, [setIsAuthorized, value, history, state]); 23 | 24 | const styles = useStyles(); 25 | 26 | return ( 27 | 28 |

Login

29 | 32 | } 33 | label="Toggle to authorize and press button to apply" 34 | /> 35 | 44 | {state && state.from && ( 45 | 46 | 47 |

The next page is Books!

48 |
49 | 50 |

The next page is Food!

51 |
52 |
53 | )} 54 |
55 | ); 56 | }; 57 | 58 | export default Login; 59 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/FirstStep/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useHistory, useLocation } from "react-router-dom"; 4 | import { Form, Field } from "react-final-form"; 5 | 6 | import Button from "@material-ui/core/Button"; 7 | 8 | import setFormData from "../../services/setFormData"; 9 | import getFormData from "../../services/getFormData"; 10 | import TextField from "../TextField"; 11 | import useStyles from "./styles"; 12 | 13 | const FirstStep = ({ title }) => { 14 | const history = useHistory(); 15 | const location = useLocation(); 16 | const [initialValues] = useState(getFormData()); 17 | 18 | const styles = useStyles(); 19 | 20 | const onSubmit = useCallback( 21 | (values) => { 22 | setFormData(values); 23 | history.push({ 24 | ...location, 25 | state: { 26 | activeStep: 1, 27 | }, 28 | }); 29 | }, 30 | [history, location] 31 | ); 32 | 33 | return ( 34 |
35 |

{title}

36 |
( 40 | 41 | 49 | 57 |
58 | 61 |
62 | 63 | )} 64 | /> 65 |
66 | ); 67 | }; 68 | 69 | FirstStep.propTypes = { 70 | title: PropTypes.string.isRequired, 71 | }; 72 | 73 | export default FirstStep; 74 | -------------------------------------------------------------------------------- /src/pages/CustomPrompt/components/SignUpForm/index.jsx: -------------------------------------------------------------------------------- 1 | /* global JSON */ 2 | import React, { useContext, useMemo } from "react"; 3 | import classNames from "classnames"; 4 | import { Prompt } from "react-router-dom"; 5 | 6 | import TextField from "@material-ui/core/TextField"; 7 | import Button from "@material-ui/core/Button"; 8 | 9 | import DeviceInfoContext from "~/contexts/DeviceInfoContext"; 10 | 11 | import usePreventReload from "./usePreventReload"; 12 | import useFormState from "./useFormState"; 13 | import useStyles from "./styles"; 14 | 15 | const SignUpForm = () => { 16 | const styles = useStyles(); 17 | const deviceInfo = useContext(DeviceInfoContext); 18 | 19 | const { fields, onChange, onSubmit } = useFormState(); 20 | 21 | const containerClasses = classNames(styles.container, { 22 | [styles.mobileContainer]: deviceInfo.isMobile 23 | }); 24 | 25 | const isEmpty = Object.keys(fields).length === 0; 26 | 27 | usePreventReload(!isEmpty); 28 | 29 | const promptMessage = useMemo(() => JSON.stringify({ fields }), [fields]); 30 | 31 | return ( 32 |
38 |

Sign Up

39 | 40 | 46 | 52 | 58 | 64 | 70 | 73 | 74 | ); 75 | }; 76 | 77 | export default SignUpForm; 78 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/PostCard/SimilarPicturesPopup/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Route, Link, useHistory } from "react-router-dom"; 3 | 4 | import Dialog from "@material-ui/core/Dialog"; 5 | import Button from "@material-ui/core/Button"; 6 | 7 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 8 | import { GET_PARAMS, GET_ENUMS } from "~/const/router"; 9 | 10 | import OriginalImage from "./OriginalImage"; 11 | import useStyles from "./styles"; 12 | 13 | const SimilarPicturesPopup = ({ pictures }) => { 14 | const styles = useStyles(); 15 | const history = useHistory(); 16 | 17 | const [similarPictureLink] = useState( 18 | usePrepareLink({ 19 | to: "/:id", 20 | isRelativePath: true, 21 | }) 22 | ); 23 | 24 | const signInLink = usePrepareLink({ 25 | query: { 26 | [GET_PARAMS.popup]: GET_ENUMS.popup.signIn, 27 | }, 28 | }); 29 | 30 | return ( 31 | <> 32 |
33 |

34 | Similar Pictures{" "} 35 | 44 |

45 |
46 | {pictures.map((picture) => { 47 | return ( 48 | 53 | similar 54 | 55 | ); 56 | })} 57 |
58 |
59 | { 62 | return ( 63 | 68 | 69 | 70 | ); 71 | }} 72 | /> 73 | 74 | ); 75 | }; 76 | 77 | export default SimilarPicturesPopup; 78 | -------------------------------------------------------------------------------- /src/pages/ResponsiveRoutes/components/Sidebar/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from "react"; 2 | import List from "@material-ui/core/List"; 3 | import ListItem from "@material-ui/core/ListItem"; 4 | import ListItemText from "@material-ui/core/ListItemText"; 5 | import Popover from "@material-ui/core/Popover"; 6 | import Divider from "@material-ui/core/Divider"; 7 | 8 | import Filter from "../Filter"; 9 | import useStyles from "./styles"; 10 | 11 | const Sidebar = () => { 12 | const styles = useStyles(); 13 | const [anchorEl, setAnchorEl] = useState(null); 14 | const [type, setType] = useState(null); 15 | 16 | const handleClick = useCallback( 17 | type => event => { 18 | setAnchorEl(event.currentTarget); 19 | setType(type); 20 | }, 21 | [] 22 | ); 23 | 24 | const handleClose = useCallback(() => { 25 | setAnchorEl(null); 26 | }, []); 27 | 28 | return ( 29 | <> 30 |

Filters

31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 69 | 70 | 71 | 72 | ); 73 | }; 74 | 75 | export default Sidebar; 76 | -------------------------------------------------------------------------------- /src/pages/Stepper/components/SecondStep/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useHistory, useLocation } from "react-router-dom"; 4 | import { Form, Field } from "react-final-form"; 5 | 6 | import IconButton from "@material-ui/core/IconButton"; 7 | import Button from "@material-ui/core/Button"; 8 | import KeyboardBackspace from "@material-ui/icons/KeyboardBackspace"; 9 | 10 | import setFormData from "../../services/setFormData"; 11 | import getFormData from "../../services/getFormData"; 12 | import TextField from "../TextField"; 13 | import useStyles from "./styles"; 14 | 15 | const SecondStep = ({ title }) => { 16 | const history = useHistory(); 17 | const location = useLocation(); 18 | const [initialValues] = useState(getFormData()); 19 | 20 | const styles = useStyles(); 21 | 22 | const onSubmit = useCallback( 23 | (values) => { 24 | setFormData(values); 25 | history.push({ 26 | ...location, 27 | state: { 28 | activeStep: 2, 29 | }, 30 | }); 31 | }, 32 | [history, location] 33 | ); 34 | 35 | const onBack = useCallback(() => { 36 | history.goBack(); 37 | }, [history]); 38 | 39 | return ( 40 |
41 |

{title}

42 | 43 | 44 | 45 |
( 49 | 50 | 58 | 66 |
67 | 70 |
71 | 72 | )} 73 | /> 74 |
75 | ); 76 | }; 77 | 78 | SecondStep.propTypes = { 79 | title: PropTypes.string.isRequired, 80 | }; 81 | 82 | export default SecondStep; 83 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/index.jsx: -------------------------------------------------------------------------------- 1 | /* global parseInt */ 2 | import React from "react"; 3 | import { useParams, useHistory, Link } from "react-router-dom"; 4 | 5 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 6 | import { GET_PARAMS, GET_ENUMS } from "~/const/router"; 7 | 8 | import Button from "@material-ui/core/Button"; 9 | import DeleteIcon from "@material-ui/icons/Delete"; 10 | import SaveIcon from "@material-ui/icons/Save"; 11 | import CloudUploadIcon from "@material-ui/icons/CloudUpload"; 12 | 13 | import posts from "../../data/posts"; 14 | import PostCard from "./PostCard"; 15 | import useStyles from "./styles"; 16 | 17 | const Post = () => { 18 | const { id } = useParams(); 19 | const history = useHistory(); 20 | const styles = useStyles(); 21 | 22 | const signInLink = usePrepareLink({ 23 | query: { 24 | [GET_PARAMS.popup]: GET_ENUMS.popup.signIn 25 | } 26 | }); 27 | const signUpLink = usePrepareLink({ 28 | query: { 29 | [GET_PARAMS.popup]: GET_ENUMS.popup.signUp 30 | } 31 | }); 32 | const notificationsLink = usePrepareLink({ 33 | query: { 34 | [GET_PARAMS.popup]: GET_ENUMS.popup.notifications 35 | } 36 | }); 37 | 38 | const post = posts.find(post => post.id === parseInt(id)); 39 | 40 | if (!post) { 41 | return null; 42 | } 43 | 44 | return ( 45 |
46 | 54 | 55 |
56 | 66 | 76 | 86 |
87 |
88 | ); 89 | }; 90 | 91 | export default Post; 92 | -------------------------------------------------------------------------------- /src/pages/TwoPopups/components/Post/index.jsx: -------------------------------------------------------------------------------- 1 | /* global parseInt */ 2 | import React from "react"; 3 | import { useParams, useHistory, Link } from "react-router-dom"; 4 | 5 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 6 | import { GET_PARAMS, GET_ENUMS } from "~/const/router"; 7 | 8 | import Button from "@material-ui/core/Button"; 9 | import DeleteIcon from "@material-ui/icons/Delete"; 10 | import SaveIcon from "@material-ui/icons/Save"; 11 | import CloudUploadIcon from "@material-ui/icons/CloudUpload"; 12 | 13 | import posts from "../../data/posts"; 14 | import PostCard from "./PostCard"; 15 | import useStyles from "./styles"; 16 | 17 | const Post = () => { 18 | const { id } = useParams(); 19 | const history = useHistory(); 20 | const styles = useStyles(); 21 | 22 | const signInLink = usePrepareLink({ 23 | query: { 24 | [GET_PARAMS.popup]: GET_ENUMS.popup.signIn, 25 | }, 26 | }); 27 | const signUpLink = usePrepareLink({ 28 | query: { 29 | [GET_PARAMS.popup]: GET_ENUMS.popup.signUp, 30 | }, 31 | }); 32 | const notificationsLink = usePrepareLink({ 33 | query: { 34 | [GET_PARAMS.popup]: GET_ENUMS.popup.notifications, 35 | }, 36 | }); 37 | 38 | const post = posts.find((post) => post.id === parseInt(id)); 39 | 40 | if (!post) { 41 | return null; 42 | } 43 | 44 | return ( 45 |
46 | 54 | 55 |
56 | 66 | 76 | 86 |
87 |
88 | ); 89 | }; 90 | 91 | export default Post; 92 | -------------------------------------------------------------------------------- /src/components/AppBar/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import ApplicationBar from "@material-ui/core/AppBar"; 5 | import Toolbar from "@material-ui/core/Toolbar"; 6 | import Typography from "@material-ui/core/Typography"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | import MenuIcon from "@material-ui/icons/Menu"; 9 | import Drawer from "@material-ui/core/Drawer"; 10 | import List from "@material-ui/core/List"; 11 | import ListItem from "@material-ui/core/ListItem"; 12 | import ListItemIcon from "@material-ui/core/ListItemIcon"; 13 | import ListItemText from "@material-ui/core/ListItemText"; 14 | import FormatListBulleted from "@material-ui/icons/FormatListBulleted"; 15 | 16 | import useStyles from "./styles"; 17 | import { TRICKS_ROUTES } from "~/const/tricks"; 18 | 19 | const AppBar = () => { 20 | const styles = useStyles(); 21 | const [isOpenedDrawer, setIsOpenedDrawer] = useState(false); 22 | 23 | const toggleDrawer = useCallback(() => { 24 | setIsOpenedDrawer(!isOpenedDrawer); 25 | }, [isOpenedDrawer]); 26 | 27 | return ( 28 | <> 29 | 30 | 31 | 37 | 38 | 39 | 45 | React Router Tricks 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {TRICKS_ROUTES.map((trick) => ( 59 | 60 | 61 | 62 | 63 | 64 | 65 | ))} 66 | 67 |
68 |
69 | 70 | ); 71 | }; 72 | 73 | export default AppBar; 74 | -------------------------------------------------------------------------------- /src/pages/PopupsRoutes/components/Post/PostCard/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Link, useHistory } from "react-router-dom"; 3 | 4 | import usePrepareLink from "~/hooks/router/usePrepareLink"; 5 | 6 | import Button from "@material-ui/core/Button"; 7 | import Card from "@material-ui/core/Card"; 8 | import CardContent from "@material-ui/core/CardContent"; 9 | import CardMedia from "@material-ui/core/CardMedia"; 10 | import Typography from "@material-ui/core/Typography"; 11 | import CardActions from "@material-ui/core/CardActions"; 12 | import Dialog from "@material-ui/core/Dialog"; 13 | 14 | import useStyles from "./styles"; 15 | import LikesPopup from "./LikesPopup"; 16 | import ViewersPopup from "./ViewersPopup"; 17 | 18 | const PostCard = ({ post }) => { 19 | const styles = useStyles(); 20 | const history = useHistory(); 21 | 22 | const likesLink = usePrepareLink({ 23 | to: "/likes", 24 | isRelativePath: true 25 | }); 26 | 27 | const viewersLink = usePrepareLink({ 28 | to: "/viewers", 29 | isRelativePath: true 30 | }); 31 | 32 | return ( 33 | <> 34 | 35 | 40 | 41 | 42 | {post.title} 43 | 44 | 45 | {post.description} 46 | 47 | 48 | 49 | 52 | 60 | 61 | 62 | { 65 | return ( 66 | 67 | 68 | 69 | ); 70 | }} 71 | /> 72 | { 75 | return ( 76 | 77 | 78 | 79 | ); 80 | }} 81 | /> 82 | 83 | ); 84 | }; 85 | 86 | export default PostCard; 87 | -------------------------------------------------------------------------------- /src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/pages/Home/components/HomePage/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import Button from "@material-ui/core/Button"; 5 | 6 | import YouTube from "~/assets/icons/youtube.svg"; 7 | import GitHub from "~/assets/icons/github.svg"; 8 | import { TRICKS_ROUTES } from "~/const/tricks"; 9 | 10 | import useStyles from "./styles"; 11 | import { IconButton } from "@material-ui/core"; 12 | 13 | const HomePage = () => { 14 | const styles = useStyles(); 15 | 16 | return ( 17 |
18 |

React Router Tricks

19 | {TRICKS_ROUTES.map((trick) => { 20 | return ( 21 |
22 | 23 | 24 |
25 |

{trick.title}

26 |

{trick.description}

27 |

28 | {Boolean(trick.enYouTube) && ( 29 | 41 | )} 42 | {Boolean(trick.ruYouTube) && ( 43 | 55 | )} 56 | 57 | {Boolean(trick.github) && ( 58 | 65 | 66 | 67 | )} 68 |

69 |
70 |
71 | ); 72 | })} 73 |
74 | ); 75 | }; 76 | 77 | export default HomePage; 78 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 42 | 47 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 72 | 73 | 77 | 78 | 87 | 91 | React-Router Tricks 92 | 93 | 94 | 95 |
96 |
97 | 107 | 108 | 112 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /config/modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const resolve = require('resolve'); 8 | 9 | /** 10 | * Get additional module paths based on the baseUrl of a compilerOptions object. 11 | * 12 | * @param {Object} options 13 | */ 14 | function getAdditionalModulePaths(options = {}) { 15 | const baseUrl = options.baseUrl; 16 | 17 | // We need to explicitly check for null and undefined (and not a falsy value) because 18 | // TypeScript treats an empty string as `.`. 19 | if (baseUrl == null) { 20 | // If there's no baseUrl set we respect NODE_PATH 21 | // Note that NODE_PATH is deprecated and will be removed 22 | // in the next major release of create-react-app. 23 | 24 | const nodePath = process.env.NODE_PATH || ''; 25 | return nodePath.split(path.delimiter).filter(Boolean); 26 | } 27 | 28 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 29 | 30 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is 31 | // the default behavior. 32 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { 33 | return null; 34 | } 35 | 36 | // Allow the user set the `baseUrl` to `appSrc`. 37 | if (path.relative(paths.appSrc, baseUrlResolved) === '') { 38 | return [paths.appSrc]; 39 | } 40 | 41 | // If the path is equal to the root directory we ignore it here. 42 | // We don't want to allow importing from the root directly as source files are 43 | // not transpiled outside of `src`. We do allow importing them with the 44 | // absolute path (e.g. `src/Components/Button.js`) but we set that up with 45 | // an alias. 46 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 47 | return null; 48 | } 49 | 50 | // Otherwise, throw an error. 51 | throw new Error( 52 | chalk.red.bold( 53 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 54 | ' Create React App does not support other values at this time.' 55 | ) 56 | ); 57 | } 58 | 59 | /** 60 | * Get webpack aliases based on the baseUrl of a compilerOptions object. 61 | * 62 | * @param {*} options 63 | */ 64 | function getWebpackAliases(options = {}) { 65 | const baseUrl = options.baseUrl; 66 | 67 | if (!baseUrl) { 68 | return {}; 69 | } 70 | 71 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 72 | 73 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 74 | return { 75 | src: paths.appSrc, 76 | }; 77 | } 78 | } 79 | 80 | /** 81 | * Get jest aliases based on the baseUrl of a compilerOptions object. 82 | * 83 | * @param {*} options 84 | */ 85 | function getJestAliases(options = {}) { 86 | const baseUrl = options.baseUrl; 87 | 88 | if (!baseUrl) { 89 | return {}; 90 | } 91 | 92 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 93 | 94 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 95 | return { 96 | '^src/(.*)$': '/src/$1', 97 | }; 98 | } 99 | } 100 | 101 | function getModules() { 102 | // Check if TypeScript is setup 103 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 104 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 105 | 106 | if (hasTsConfig && hasJsConfig) { 107 | throw new Error( 108 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 109 | ); 110 | } 111 | 112 | let config; 113 | 114 | // If there's a tsconfig.json we assume it's a 115 | // TypeScript project and set up the config 116 | // based on tsconfig.json 117 | if (hasTsConfig) { 118 | const ts = require(resolve.sync('typescript', { 119 | basedir: paths.appNodeModules, 120 | })); 121 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; 122 | // Otherwise we'll check if there is jsconfig.json 123 | // for non TS projects. 124 | } else if (hasJsConfig) { 125 | config = require(paths.appJsConfig); 126 | } 127 | 128 | config = config || {}; 129 | const options = config.compilerOptions || {}; 130 | 131 | const additionalModulePaths = getAdditionalModulePaths(options); 132 | 133 | return { 134 | additionalModulePaths: additionalModulePaths, 135 | webpackAliases: getWebpackAliases(options), 136 | jestAliases: getJestAliases(options), 137 | hasTsConfig, 138 | }; 139 | } 140 | 141 | module.exports = getModules(); 142 | -------------------------------------------------------------------------------- /src/pages/RestorePreventedRoute/components/Food/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | 4 | import Card from "@material-ui/core/Card"; 5 | import CardHeader from "@material-ui/core/CardHeader"; 6 | import CardMedia from "@material-ui/core/CardMedia"; 7 | import CardContent from "@material-ui/core/CardContent"; 8 | import CardActions from "@material-ui/core/CardActions"; 9 | import Collapse from "@material-ui/core/Collapse"; 10 | import Avatar from "@material-ui/core/Avatar"; 11 | import IconButton from "@material-ui/core/IconButton"; 12 | import Typography from "@material-ui/core/Typography"; 13 | import FavoriteIcon from "@material-ui/icons/Favorite"; 14 | import ShareIcon from "@material-ui/icons/Share"; 15 | import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; 16 | import MoreVertIcon from "@material-ui/icons/MoreVert"; 17 | 18 | import useStyles from "./styles"; 19 | 20 | const Food = () => { 21 | const styles = useStyles(); 22 | 23 | const [expanded, setExpanded] = React.useState(false); 24 | 25 | const handleExpandClick = () => { 26 | setExpanded(!expanded); 27 | }; 28 | 29 | return ( 30 | 31 | 34 | R 35 | 36 | } 37 | action={ 38 | 39 | 40 | 41 | } 42 | title="Shrimp and Chorizo Paella" 43 | subheader="September 14, 2016" 44 | /> 45 | 50 | 51 | 52 | This impressive paella is a perfect party dish and a fun meal to cook 53 | together with your guests. Add 1 cup of frozen peas along with the 54 | mussels, if you like. 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 72 | 73 | 74 | 75 | 76 | 77 | Method: 78 | 79 | Heat 1/2 cup of the broth in a pot until simmering, add saffron and 80 | set aside for 10 minutes. 81 | 82 | 83 | Heat oil in a (14- to 16-inch) paella pan or a large, deep skillet 84 | over medium-high heat. Add chicken, shrimp and chorizo, and cook, 85 | stirring occasionally until lightly browned, 6 to 8 minutes. 86 | Transfer shrimp to a large plate and set aside, leaving chicken and 87 | chorizo in the pan. Add pimentón, bay leaves, garlic, tomatoes, 88 | onion, salt and pepper, and cook, stirring often until thickened and 89 | fragrant, about 10 minutes. Add saffron broth and remaining 4 1/2 90 | cups chicken broth; bring to a boil. 91 | 92 | 93 | Add rice and stir very gently to distribute. Top with artichokes and 94 | peppers, and cook without stirring, until most of the liquid is 95 | absorbed, 15 to 18 minutes. Reduce heat to medium-low, add reserved 96 | shrimp and mussels, tucking them down into the rice, and cook again 97 | without stirring, until mussels have opened and rice is just tender, 98 | 5 to 7 minutes more. (Discard any mussels that don’t open.) 99 | 100 | 101 | Set aside off of the heat to let rest for 10 minutes, and then 102 | serve. 103 | 104 | 105 | 106 | 107 | ); 108 | }; 109 | 110 | export default Food; 111 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-dom-tricks", 3 | "homepage": "https://sin9k.github.io", 4 | "version": "1.0.0", 5 | "private": false, 6 | "dependencies": { 7 | "@babel/core": "7.7.4", 8 | "@material-ui/core": "^4.8.3", 9 | "@material-ui/icons": "^4.5.1", 10 | "@svgr/webpack": "4.3.3", 11 | "@testing-library/jest-dom": "^4.2.4", 12 | "@testing-library/react": "^9.3.2", 13 | "@testing-library/user-event": "^7.1.2", 14 | "@typescript-eslint/eslint-plugin": "^2.8.0", 15 | "@typescript-eslint/parser": "^2.8.0", 16 | "babel-eslint": "10.0.3", 17 | "babel-jest": "^24.9.0", 18 | "babel-loader": "8.0.6", 19 | "babel-plugin-named-asset-import": "^0.3.5", 20 | "babel-preset-react-app": "^9.1.0", 21 | "camelcase": "^5.3.1", 22 | "case-sensitive-paths-webpack-plugin": "2.2.0", 23 | "classnames": "^2.2.6", 24 | "css-loader": "3.2.0", 25 | "dotenv": "8.2.0", 26 | "dotenv-expand": "5.1.0", 27 | "eslint": "^6.6.0", 28 | "eslint-config-react-app": "^5.1.0", 29 | "eslint-loader": "3.0.2", 30 | "eslint-plugin-flowtype": "3.13.0", 31 | "eslint-plugin-import": "2.18.2", 32 | "eslint-plugin-jsx-a11y": "6.2.3", 33 | "eslint-plugin-react": "7.16.0", 34 | "eslint-plugin-react-hooks": "^1.6.1", 35 | "file-loader": "4.3.0", 36 | "final-form": "^4.19.1", 37 | "fs-extra": "^8.1.0", 38 | "html-webpack-plugin": "4.0.0-beta.5", 39 | "identity-obj-proxy": "3.0.0", 40 | "jest": "24.9.0", 41 | "jest-environment-jsdom-fourteen": "0.1.0", 42 | "jest-resolve": "24.9.0", 43 | "jest-watch-typeahead": "0.4.2", 44 | "mini-css-extract-plugin": "0.8.0", 45 | "optimize-css-assets-webpack-plugin": "5.0.3", 46 | "pnp-webpack-plugin": "1.5.0", 47 | "postcss-flexbugs-fixes": "4.1.0", 48 | "postcss-loader": "3.0.0", 49 | "postcss-normalize": "8.0.1", 50 | "postcss-preset-env": "6.7.0", 51 | "postcss-safe-parser": "4.0.1", 52 | "react": "^16.12.0", 53 | "react-app-polyfill": "^1.0.5", 54 | "react-dev-utils": "^10.0.0", 55 | "react-dom": "^16.12.0", 56 | "react-final-form": "^6.4.0", 57 | "react-redux": "^7.2.0", 58 | "react-router-dom": "^5.1.2", 59 | "react-svg-loader": "^3.0.3", 60 | "redux": "^4.0.5", 61 | "redux-actions": "^2.6.5", 62 | "reselect": "^4.0.0", 63 | "resolve": "1.12.2", 64 | "resolve-url-loader": "3.1.1", 65 | "sass-loader": "8.0.0", 66 | "semver": "6.3.0", 67 | "style-loader": "1.0.0", 68 | "terser-webpack-plugin": "2.2.1", 69 | "ts-pnp": "1.1.5", 70 | "url-loader": "2.3.0", 71 | "webpack": "4.41.2", 72 | "webpack-dev-server": "3.9.0", 73 | "webpack-manifest-plugin": "2.2.0", 74 | "workbox-webpack-plugin": "4.3.1" 75 | }, 76 | "scripts": { 77 | "start": "node scripts/start.js", 78 | "build": "node scripts/build.js", 79 | "test": "node scripts/test.js", 80 | "predeploy": "npm run build", 81 | "deploy": "gh-pages -d build" 82 | }, 83 | "eslintConfig": { 84 | "extends": "react-app" 85 | }, 86 | "browserslist": { 87 | "production": [ 88 | ">0.2%", 89 | "not dead", 90 | "not op_mini all" 91 | ], 92 | "development": [ 93 | "last 1 chrome version", 94 | "last 1 firefox version", 95 | "last 1 safari version" 96 | ] 97 | }, 98 | "jest": { 99 | "roots": [ 100 | "/src" 101 | ], 102 | "collectCoverageFrom": [ 103 | "src/**/*.{js,jsx,ts,tsx}", 104 | "!src/**/*.d.ts" 105 | ], 106 | "setupFiles": [ 107 | "react-app-polyfill/jsdom" 108 | ], 109 | "setupFilesAfterEnv": [], 110 | "testMatch": [ 111 | "/src/**/__tests__/**/*.{js,jsx,ts,tsx}", 112 | "/src/**/*.{spec,test}.{js,jsx,ts,tsx}" 113 | ], 114 | "testEnvironment": "jest-environment-jsdom-fourteen", 115 | "transform": { 116 | "^.+\\.(js|jsx|ts|tsx)$": "/node_modules/babel-jest", 117 | "^.+\\.css$": "/config/jest/cssTransform.js", 118 | "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "/config/jest/fileTransform.js" 119 | }, 120 | "transformIgnorePatterns": [ 121 | "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$", 122 | "^.+\\.module\\.(css|sass|scss)$" 123 | ], 124 | "modulePaths": [], 125 | "moduleNameMapper": { 126 | "^react-native$": "react-native-web", 127 | "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy" 128 | }, 129 | "moduleFileExtensions": [ 130 | "web.js", 131 | "js", 132 | "web.ts", 133 | "ts", 134 | "web.tsx", 135 | "tsx", 136 | "json", 137 | "web.jsx", 138 | "jsx", 139 | "node" 140 | ], 141 | "watchPlugins": [ 142 | "jest-watch-typeahead/filename", 143 | "jest-watch-typeahead/testname" 144 | ] 145 | }, 146 | "babel": { 147 | "presets": [ 148 | "react-app" 149 | ] 150 | }, 151 | "devDependencies": { 152 | "gh-pages": "^2.2.0" 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /scripts/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'development'; 5 | process.env.NODE_ENV = 'development'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | 18 | const fs = require('fs'); 19 | const chalk = require('react-dev-utils/chalk'); 20 | const webpack = require('webpack'); 21 | const WebpackDevServer = require('webpack-dev-server'); 22 | const clearConsole = require('react-dev-utils/clearConsole'); 23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 24 | const { 25 | choosePort, 26 | createCompiler, 27 | prepareProxy, 28 | prepareUrls, 29 | } = require('react-dev-utils/WebpackDevServerUtils'); 30 | const openBrowser = require('react-dev-utils/openBrowser'); 31 | const paths = require('../config/paths'); 32 | const configFactory = require('../config/webpack.config'); 33 | const createDevServerConfig = require('../config/webpackDevServer.config'); 34 | 35 | const useYarn = fs.existsSync(paths.yarnLockFile); 36 | const isInteractive = process.stdout.isTTY; 37 | 38 | // Warn and crash if required files are missing 39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 40 | process.exit(1); 41 | } 42 | 43 | // Tools like Cloud9 rely on this. 44 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 45 | const HOST = process.env.HOST || '0.0.0.0'; 46 | 47 | if (process.env.HOST) { 48 | console.log( 49 | chalk.cyan( 50 | `Attempting to bind to HOST environment variable: ${chalk.yellow( 51 | chalk.bold(process.env.HOST) 52 | )}` 53 | ) 54 | ); 55 | console.log( 56 | `If this was unintentional, check that you haven't mistakenly set it in your shell.` 57 | ); 58 | console.log( 59 | `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}` 60 | ); 61 | console.log(); 62 | } 63 | 64 | // We require that you explicitly set browsers and do not fall back to 65 | // browserslist defaults. 66 | const { checkBrowsers } = require('react-dev-utils/browsersHelper'); 67 | checkBrowsers(paths.appPath, isInteractive) 68 | .then(() => { 69 | // We attempt to use the default port but if it is busy, we offer the user to 70 | // run on a different port. `choosePort()` Promise resolves to the next free port. 71 | return choosePort(HOST, DEFAULT_PORT); 72 | }) 73 | .then(port => { 74 | if (port == null) { 75 | // We have not found a port. 76 | return; 77 | } 78 | const config = configFactory('development'); 79 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 80 | const appName = require(paths.appPackageJson).name; 81 | const useTypeScript = fs.existsSync(paths.appTsConfig); 82 | const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; 83 | const urls = prepareUrls(protocol, HOST, port); 84 | const devSocket = { 85 | warnings: warnings => 86 | devServer.sockWrite(devServer.sockets, 'warnings', warnings), 87 | errors: errors => 88 | devServer.sockWrite(devServer.sockets, 'errors', errors), 89 | }; 90 | // Create a webpack compiler that is configured with custom messages. 91 | const compiler = createCompiler({ 92 | appName, 93 | config, 94 | devSocket, 95 | urls, 96 | useYarn, 97 | useTypeScript, 98 | tscCompileOnError, 99 | webpack, 100 | }); 101 | // Load proxy config 102 | const proxySetting = require(paths.appPackageJson).proxy; 103 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic); 104 | // Serve webpack assets generated by the compiler over a web server. 105 | const serverConfig = createDevServerConfig( 106 | proxyConfig, 107 | urls.lanUrlForConfig 108 | ); 109 | const devServer = new WebpackDevServer(compiler, serverConfig); 110 | // Launch WebpackDevServer. 111 | devServer.listen(port, HOST, err => { 112 | if (err) { 113 | return console.log(err); 114 | } 115 | if (isInteractive) { 116 | clearConsole(); 117 | } 118 | 119 | // We used to support resolving modules according to `NODE_PATH`. 120 | // This now has been deprecated in favor of jsconfig/tsconfig.json 121 | // This lets you use absolute paths in imports inside large monorepos: 122 | if (process.env.NODE_PATH) { 123 | console.log( 124 | chalk.yellow( 125 | 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.' 126 | ) 127 | ); 128 | console.log(); 129 | } 130 | 131 | console.log(chalk.cyan('Starting the development server...\n')); 132 | openBrowser(urls.localUrlForBrowser); 133 | }); 134 | 135 | ['SIGINT', 'SIGTERM'].forEach(function(sig) { 136 | process.on(sig, function() { 137 | devServer.close(); 138 | process.exit(); 139 | }); 140 | }); 141 | }) 142 | .catch(err => { 143 | if (err && err.message) { 144 | console.log(err.message); 145 | } 146 | process.exit(1); 147 | }); 148 | -------------------------------------------------------------------------------- /src/const/tricks.js: -------------------------------------------------------------------------------- 1 | import TabletMac from "@material-ui/icons/TabletMac"; 2 | import Web from "@material-ui/icons/Web"; 3 | import AspectRatio from "@material-ui/icons/AspectRatio"; 4 | import Cancel from "@material-ui/icons/Cancel"; 5 | import Save from "@material-ui/icons/Save"; 6 | import HdrWeak from "@material-ui/icons/HdrWeak"; 7 | import DynamicFeed from "@material-ui/icons/DynamicFeed"; 8 | 9 | import responsiveRoutes from "~/assets/images/responsive-routes.png"; 10 | import popusRoutes from "~/assets/images/popus-routes.png"; 11 | import customPrompt from "~/assets/images/custom-prompt.png"; 12 | import closePopup from "~/assets/images/close-popup.png"; 13 | import redirectAfterLogin from "~/assets/images/redirect-after-login.png"; 14 | import multiStepForm from "~/assets/images/multi-step-form.png"; 15 | import twoPopups from "~/assets/images/two-popups.png"; 16 | 17 | export const TRICKS_ROUTES = [ 18 | { 19 | title: "Responsive Routes", 20 | shortTitle: "Responsive Routes", 21 | description: 22 | "During development of adaptive website for all platforms (desktop / mobile). In some cases, one page in the desktop version is equivalent to whole set of screens in the mobile version. In this video you will know how is it possible to switch one route on the desktop to 3 routes on the mobile phone in runtime", 23 | url: "/responsive-routes", 24 | Icon: TabletMac, 25 | image: responsiveRoutes, 26 | enYouTube: "https://youtu.be/X3zE2eIpUmk", 27 | ruYouTube: "https://youtu.be/FtYzwa0DjW8", 28 | github: 29 | "https://github.com/Sin9k/react-router-dom-tricks#responsive-routes", 30 | }, 31 | { 32 | title: "Managing pop-ups using react-router", 33 | shortTitle: "Pop-up Routes", 34 | description: 35 | "In this video, we will show you several ways how to manage pop-ups using React-router. In our test application, you will see how to make a specific URL for your pop-ups and share links with others, how to control closing animation, and also how to deal with other problems related to managing pop-ups!", 36 | url: "/popups-routes", 37 | Icon: Web, 38 | image: popusRoutes, 39 | enYouTube: "https://youtu.be/RYo3kwdDdBI", 40 | ruYouTube: "https://youtu.be/4YHnZSMo9vo", 41 | github: "https://github.com/Sin9k/react-router-dom-tricks#popups-routes", 42 | }, 43 | { 44 | title: "How to close pop-up via history.goBack", 45 | shortTitle: "Close Popup", 46 | description: 47 | 'If you carefully watched the video "Managing pop-ups using react-router", you probably were outraged by my negligence when closing pop-ups using history.goBack. This was not an accident, there is no simple solution to this problem, so we created a separate video on how to make sure that users who came to your site through a direct link to the pop-up do not leave the site when trying to click the cross', 48 | url: "/close-popup", 49 | Icon: Cancel, 50 | image: closePopup, 51 | enYouTube: "https://youtu.be/tl5_k6YBRKg", 52 | ruYouTube: "https://youtu.be/fqYk-b1lhyI", 53 | github: 54 | "https://github.com/Sin9k/react-router-dom-tricks#how-to-use-historygoback-for-closing-popup", 55 | }, 56 | { 57 | title: "Custom React Router Prompts", 58 | shortTitle: "Custom Prompt", 59 | description: 60 | "In this video, we will show you how useful can be Prompt from the React-Router library. In our Demo, you can see how to implement your custom Prompt component instead of the system message window, dive into the details of how Prompt works, find out whether you can prevent the page reloading and a lot of other useful information that you will definitely need for your high-quality application.", 61 | url: "/custom-prompt", 62 | Icon: AspectRatio, 63 | image: customPrompt, 64 | enYouTube: "https://youtu.be/ZE5I9RbMaGY", 65 | ruYouTube: "https://youtu.be/qDJ2OMcz8is", 66 | github: "https://github.com/Sin9k/react-router-dom-tricks#custom-prompt", 67 | }, 68 | { 69 | title: "Redirect After Login", 70 | shortTitle: "Redirect After Login", 71 | description: 72 | "In this video, we will consider another react-router trick. We’ll tell you how to restore the previous URL after a redirect. We will show you how to manage react-router properly in order to be able to create authorized pages. With the help of our demo you can find how to redirect unauthorized users to the sign in page firstly when they go to the authorized page, and only then redirect them exactly to the page that they originally wanted. And also, a lot of other interesting things!", 73 | url: "/restore-prevented-route", 74 | Icon: Save, 75 | image: redirectAfterLogin, 76 | enYouTube: "https://youtu.be/VI3Mo6FJCZA", 77 | ruYouTube: "https://youtu.be/7Ot95R_TP4g", 78 | github: 79 | "https://github.com/Sin9k/react-router-dom-tricks#redirect-after-login", 80 | }, 81 | { 82 | title: "Perfect stepper", 83 | shortTitle: "Perfect stepper", 84 | description: 85 | "On my opinion it's the best multi step form what a lot of you have ever seen. It handles a lot of edge cases of business requirements and is able to scale to your specific requirements", 86 | url: "/stepper", 87 | Icon: HdrWeak, 88 | image: multiStepForm, 89 | enYouTube: "https://youtu.be/-j8s0RNOZ8U", 90 | ruYouTube: "https://youtu.be/d3SH1H-shlg", 91 | github: "https://github.com/Sin9k/react-router-dom-tricks", 92 | }, 93 | { 94 | title: "Managing two pop-ups at the same time using react-router", 95 | shortTitle: "Two pop-ups at the same time", 96 | description: 97 | "We figured out how to manage pop-ups using react-router in one of the previous tricks. But it's not covered all cases and now we will consider how to manage two or more pop-ups at the same time via react-router.", 98 | url: "/two-popups", 99 | Icon: DynamicFeed, 100 | image: twoPopups, 101 | enYouTube: "https://youtu.be/cWke1z6VgxM", 102 | ruYouTube: "https://youtu.be/pDDtOwETx_0", 103 | github: 104 | "https://github.com/Sin9k/react-router-dom-tricks#managing-two-or-more-pop-ups-via-react-router", 105 | }, 106 | ]; 107 | --------------------------------------------------------------------------------