- memoize all components

- implement metadata context
This commit is contained in:
Amruth Pillai 2020-07-09 14:00:18 +05:30
parent 9e98da038c
commit 3aaef5f730
71 changed files with 489 additions and 396 deletions

View File

@ -1,5 +1,5 @@
{ {
"semi": true, "semi": true,
"trailingComma": "all", "singleQuote": true,
"singleQuote": true "trailingComma": "all"
} }

View File

@ -4,8 +4,10 @@
}, },
"[javascriptreact]": { "[javascriptreact]": {
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.organizeImports": true, "source.organizeImports": true
"source.fixAll": true
} }
},
"editor.codeActionsOnSave": {
"source.fixAll": true
} }
} }

View File

@ -5,10 +5,10 @@ import 'firebase/database';
import 'firebase/storage'; import 'firebase/storage';
import React from 'react'; import React from 'react';
import { DatabaseProvider } from './src/contexts/DatabaseContext'; import { DatabaseProvider } from './src/contexts/DatabaseContext';
import { MetadataProvider } from './src/contexts/MetadataContext';
import { ModalProvider } from './src/contexts/ModalContext'; import { ModalProvider } from './src/contexts/ModalContext';
import { ResumeProvider } from './src/contexts/ResumeContext'; import { ResumeProvider } from './src/contexts/ResumeContext';
import { StorageProvider } from './src/contexts/StorageContext'; import { StorageProvider } from './src/contexts/StorageContext';
import { TemplateProvider } from './src/contexts/TemplateContext';
import { ThemeProvider } from './src/contexts/ThemeContext'; import { ThemeProvider } from './src/contexts/ThemeContext';
import { UserProvider } from './src/contexts/UserContext'; import { UserProvider } from './src/contexts/UserContext';
import './src/styles/colors.css'; import './src/styles/colors.css';
@ -32,9 +32,9 @@ export const wrapRootElement = ({ element }) => (
<UserProvider> <UserProvider>
<DatabaseProvider> <DatabaseProvider>
<ResumeProvider> <ResumeProvider>
<StorageProvider> <MetadataProvider>
<TemplateProvider>{element}</TemplateProvider> <StorageProvider>{element}</StorageProvider>
</StorageProvider> </MetadataProvider>
</ResumeProvider> </ResumeProvider>
</DatabaseProvider> </DatabaseProvider>
</UserProvider> </UserProvider>

View File

@ -1,13 +1,13 @@
import React, { useContext } from 'react'; import React, { memo } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { useSelector } from '../../../contexts/ResumeContext'; import { useSelector as useMetadataSelector } from '../../../contexts/MetadataContext';
import TemplateContext from '../../../contexts/TemplateContext'; import { useSelector as useResumeSelector } from '../../../contexts/ResumeContext';
import Onyx from '../../../templates/Onyx'; import Onyx from '../../../templates/Onyx';
import styles from './Artboard.module.css'; import styles from './Artboard.module.css';
const Artboard = () => { const Artboard = () => {
const { selected, blocks, colors } = useContext(TemplateContext); const { template, layout, colors } = useMetadataSelector((store) => store);
const state = useSelector((store) => store); const state = useResumeSelector((store) => store);
const { id, name } = state; const { id, name } = state;
return ( return (
@ -18,12 +18,12 @@ const Artboard = () => {
</Helmet> </Helmet>
<div id="artboard" className={styles.container}> <div id="artboard" className={styles.container}>
{selected === 'Onyx' && ( {template === 'Onyx' && (
<Onyx data={state} layout={blocks} colors={colors} /> <Onyx data={state} layout={layout} colors={colors} />
)} )}
</div> </div>
</div> </div>
); );
}; };
export default Artboard; export default memo(Artboard);

View File

@ -1,5 +1,5 @@
import { Link } from 'gatsby'; import { Link } from 'gatsby';
import React from 'react'; import React, { memo } from 'react';
import sections from '../../../data/leftSections'; import sections from '../../../data/leftSections';
import Avatar from '../../shared/Avatar'; import Avatar from '../../shared/Avatar';
import Logo from '../../shared/Logo'; import Logo from '../../shared/Logo';
@ -26,4 +26,4 @@ const LeftNavbar = () => (
</div> </div>
); );
export default LeftNavbar; export default memo(LeftNavbar);

View File

@ -1,22 +1,70 @@
import React from 'react'; import React, { Fragment, memo } from 'react';
import { Element } from 'react-scroll'; import { Element } from 'react-scroll';
import sections from '../../../data/leftSections'; import sections from '../../../data/leftSections';
import Awards from '../sections/Awards';
import Certifications from '../sections/Certifications';
import Education from '../sections/Education';
import Hobbies from '../sections/Hobbies';
import Languages from '../sections/Languages';
import Objective from '../sections/Objective';
import Profile from '../sections/Profile';
import References from '../sections/References';
import Skills from '../sections/Skills';
import Social from '../sections/Social';
import Work from '../sections/Work';
import LeftNavbar from './LeftNavbar'; import LeftNavbar from './LeftNavbar';
import styles from './LeftSidebar.module.css'; import styles from './LeftSidebar.module.css';
const LeftSidebar = () => ( const getComponent = (id) => {
<div className="flex"> switch (id) {
<LeftNavbar /> case 'profile':
return Profile;
case 'social':
return Social;
case 'objective':
return Objective;
case 'work':
return Work;
case 'education':
return Education;
case 'awards':
return Awards;
case 'certifications':
return Certifications;
case 'skills':
return Skills;
case 'hobbies':
return Hobbies;
case 'languages':
return Languages;
case 'references':
return References;
default:
throw new Error();
}
};
<div id="LeftSidebar" className={styles.container}> const LeftSidebar = () => {
{sections.map(({ id, name, event, component: Component }) => ( return (
<Element key={id} name={id}> <div className="flex">
<Component id={id} name={name} event={event} /> <LeftNavbar />
<hr />
</Element> <div id="LeftSidebar" className={styles.container}>
))} {sections.map(({ id, name, event }) => {
const Component = getComponent(id);
return (
<Fragment key={id}>
<Element name={id}>
<Component id={id} name={name} event={event} />
</Element>
<hr />
</Fragment>
);
})}
</div>
</div> </div>
</div> );
); };
export default LeftSidebar; export default memo(LeftSidebar);

View File

@ -4,7 +4,7 @@
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none; scrollbar-width: none;
@apply w-full h-screen overflow-scroll p-8; @apply w-full h-screen overflow-scroll p-8;
@apply grid gap-6; @apply grid gap-8;
} }
.container::-webkit-scrollbar { .container::-webkit-scrollbar {

View File

@ -1,7 +1,7 @@
import React from 'react'; import React, { memo } from 'react';
const EmptyList = () => ( const EmptyList = () => (
<div className="py-6 opacity-75 text-center">This list is empty.</div> <div className="py-6 opacity-75 text-center">This list is empty.</div>
); );
export default EmptyList; export default memo(EmptyList);

View File

@ -1,5 +1,5 @@
import { get, isEmpty } from 'lodash'; import { get, isEmpty } from 'lodash';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import { MdAdd } from 'react-icons/md'; import { MdAdd } from 'react-icons/md';
import ModalContext from '../../../contexts/ModalContext'; import ModalContext from '../../../contexts/ModalContext';
import { useSelector } from '../../../contexts/ResumeContext'; import { useSelector } from '../../../contexts/ResumeContext';
@ -64,4 +64,4 @@ const List = ({
); );
}; };
export default List; export default memo(List);

View File

@ -1,5 +1,5 @@
import { Menu, MenuItem } from '@material-ui/core'; import { Menu, MenuItem } from '@material-ui/core';
import React, { useState } from 'react'; import React, { memo, useState } from 'react';
import { IoIosArrowDown, IoIosArrowUp } from 'react-icons/io'; import { IoIosArrowDown, IoIosArrowUp } from 'react-icons/io';
import { MdMoreVert } from 'react-icons/md'; import { MdMoreVert } from 'react-icons/md';
import { useDispatch } from '../../../contexts/ResumeContext'; import { useDispatch } from '../../../contexts/ResumeContext';
@ -108,4 +108,4 @@ const ListItem = ({
); );
}; };
export default ListItem; export default memo(ListItem);

View File

@ -1,16 +1,16 @@
import React from 'react'; import React, { memo } from 'react';
import { MdPerson } from 'react-icons/md'; import sections from '../../../data/rightSections';
import SectionIcon from '../../shared/SectionIcon';
import styles from './RightNavbar.module.css'; import styles from './RightNavbar.module.css';
import SyncIndicator from './SyncIndicator'; import SyncIndicator from './SyncIndicator';
const RightNavbar = () => { const RightNavbar = () => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className="grid grid-cols-1 gap-6"> <div className="grid grid-cols-1 gap-8">
<MdPerson {sections.map((x) => (
className="text-secondary-dark hover:text-primary" <SectionIcon key={x.id} section={x} containerId="RightSidebar" />
size="20px" ))}
/>
</div> </div>
<hr className="mt-auto my-6" /> <hr className="mt-auto my-6" />
@ -20,4 +20,4 @@ const RightNavbar = () => {
); );
}; };
export default RightNavbar; export default memo(RightNavbar);

View File

@ -1,14 +1,35 @@
import React from 'react'; import React, { Fragment, memo } from 'react';
import { Element } from 'react-scroll';
import sections from '../../../data/rightSections';
import Layout from '../sections/Layout'; import Layout from '../sections/Layout';
import RightNavbar from './RightNavbar'; import RightNavbar from './RightNavbar';
import styles from './RightSidebar.module.css'; import styles from './RightSidebar.module.css';
const getComponent = (id) => {
switch (id) {
case 'layout':
return Layout;
default:
throw new Error();
}
};
const RightSidebar = () => { const RightSidebar = () => {
return ( return (
<div className="flex"> <div className="flex">
<div className={styles.container}> <div id="RightSidebar" className={styles.container}>
<Layout /> {sections.map(({ id, name, event }) => {
<hr /> const Component = getComponent(id);
return (
<Fragment key={id}>
<Element name={id}>
<Component id={id} name={name} event={event} />
</Element>
<hr />
</Fragment>
);
})}
</div> </div>
<RightNavbar /> <RightNavbar />
@ -16,4 +37,4 @@ const RightSidebar = () => {
); );
}; };
export default RightSidebar; export default memo(RightSidebar);

View File

@ -1,5 +1,5 @@
import cx from 'classnames'; import cx from 'classnames';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import { MdSync, MdSyncDisabled } from 'react-icons/md'; import { MdSync, MdSyncDisabled } from 'react-icons/md';
import DatabaseContext from '../../../contexts/DatabaseContext'; import DatabaseContext from '../../../contexts/DatabaseContext';
@ -17,4 +17,4 @@ const SyncIndicator = () => {
); );
}; };
export default SyncIndicator; export default memo(SyncIndicator);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -20,4 +20,4 @@ const Awards = ({ id, name, event }) => {
); );
}; };
export default Awards; export default memo(Awards);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -20,4 +20,4 @@ const Certifications = ({ id, name, event }) => {
); );
}; };
export default Certifications; export default memo(Certifications);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -20,4 +20,4 @@ const Education = ({ id, name, event }) => {
); );
}; };
export default Education; export default memo(Education);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -14,4 +14,4 @@ const Hobbies = ({ id, name, event }) => {
); );
}; };
export default Hobbies; export default memo(Hobbies);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -14,4 +14,4 @@ const Languages = ({ id, name, event }) => {
); );
}; };
export default Languages; export default memo(Languages);

View File

@ -1,11 +1,36 @@
import React, { useContext } from 'react'; import React, { memo } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import TemplateContext from '../../../contexts/TemplateContext'; import { useDispatch, useSelector } from '../../../contexts/MetadataContext';
import { move, reorder } from '../../../utils';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import styles from './Layout.module.css'; import styles from './Layout.module.css';
const Layout = () => { const Layout = () => {
const { blocks, onDragEnd } = useContext(TemplateContext); const blocks = useSelector((state) => state.layout);
const dispatch = useDispatch();
const onDragEnd = (result) => {
const { source, destination } = result;
if (!destination) {
return;
}
const sInd = +source.droppableId;
const dInd = +destination.droppableId;
if (sInd === dInd) {
const items = reorder(blocks[sInd], source.index, destination.index);
const newState = [...blocks];
newState[sInd] = items;
dispatch({ type: 'set_layout', payload: newState });
} else {
const newResult = move(blocks[sInd], blocks[dInd], source, destination);
const newState = [...blocks];
newState[sInd] = newResult[sInd];
newState[dInd] = newResult[dInd];
dispatch({ type: 'set_layout', payload: newState });
}
};
return ( return (
<section> <section>
@ -60,4 +85,4 @@ const Layout = () => {
); );
}; };
export default Layout; export default memo(Layout);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import Input from '../../shared/Input'; import Input from '../../shared/Input';
@ -12,4 +12,4 @@ const Objective = () => {
); );
}; };
export default Objective; export default memo(Objective);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import Input from '../../shared/Input'; import Input from '../../shared/Input';
import PhotoUpload from '../../shared/PhotoUpload'; import PhotoUpload from '../../shared/PhotoUpload';
@ -44,4 +44,4 @@ const Profile = () => {
); );
}; };
export default Profile; export default memo(Profile);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -20,4 +20,4 @@ const References = ({ id, name, event }) => {
); );
}; };
export default References; export default memo(References);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -14,4 +14,4 @@ const Skills = ({ id, name, event }) => {
); );
}; };
export default Skills; export default memo(Skills);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -19,4 +19,4 @@ const Social = ({ id, name, event }) => {
); );
}; };
export default Social; export default memo(Social);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import Heading from '../../shared/Heading'; import Heading from '../../shared/Heading';
import List from '../lists/List'; import List from '../lists/List';
@ -20,4 +20,4 @@ const Work = ({ id, name, event }) => {
); );
}; };
export default Work; export default memo(Work);

View File

@ -1,4 +1,4 @@
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import { MdAdd } from 'react-icons/md'; import { MdAdd } from 'react-icons/md';
import ModalContext from '../../contexts/ModalContext'; import ModalContext from '../../contexts/ModalContext';
import { handleKeyUp } from '../../utils'; import { handleKeyUp } from '../../utils';
@ -30,4 +30,4 @@ const CreateResume = () => {
); );
}; };
export default CreateResume; export default memo(CreateResume);

View File

@ -1,7 +1,7 @@
import { Menu, MenuItem } from '@material-ui/core'; import { Menu, MenuItem } from '@material-ui/core';
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import moment from 'moment'; import moment from 'moment';
import React, { useContext, useState } from 'react'; import React, { memo, useContext, useState } from 'react';
import { MdMoreHoriz, MdOpenInNew } from 'react-icons/md'; import { MdMoreHoriz, MdOpenInNew } from 'react-icons/md';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import DatabaseContext from '../../contexts/DatabaseContext'; import DatabaseContext from '../../contexts/DatabaseContext';
@ -78,4 +78,4 @@ const ResumePreview = ({ resume }) => {
); );
}; };
export default ResumePreview; export default memo(ResumePreview);

View File

@ -1,5 +1,5 @@
import { Link, navigate } from 'gatsby'; import { Link, navigate } from 'gatsby';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import UserContext from '../../contexts/UserContext'; import UserContext from '../../contexts/UserContext';
import Avatar from '../shared/Avatar'; import Avatar from '../shared/Avatar';
import Logo from '../shared/Logo'; import Logo from '../shared/Logo';
@ -35,4 +35,4 @@ const TopNavbar = () => {
); );
}; };
export default TopNavbar; export default memo(TopNavbar);

View File

@ -1,5 +1,5 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import { FaGithub } from 'react-icons/fa'; import { FaGithub } from 'react-icons/fa';
import ModalContext from '../../contexts/ModalContext'; import ModalContext from '../../contexts/ModalContext';
import ThemeContext from '../../contexts/ThemeContext'; import ThemeContext from '../../contexts/ThemeContext';
@ -49,4 +49,4 @@ const Hero = () => {
); );
}; };
export default Hero; export default memo(Hero);

View File

@ -1,5 +1,5 @@
import { Fade, Modal } from '@material-ui/core'; import { Fade, Modal } from '@material-ui/core';
import React from 'react'; import React, { memo } from 'react';
import { getRandomTip } from '../../data/tips'; import { getRandomTip } from '../../data/tips';
import Logo from '../shared/Logo'; import Logo from '../shared/Logo';
@ -18,4 +18,4 @@ const LoadingScreen = () => {
); );
}; };
export default LoadingScreen; export default memo(LoadingScreen);

View File

@ -1,5 +1,5 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import UserContext from '../../contexts/UserContext'; import UserContext from '../../contexts/UserContext';
import LoadingScreen from './LoadingScreen'; import LoadingScreen from './LoadingScreen';
@ -18,4 +18,4 @@ const PrivateRoute = ({ component: Component, location, ...props }) => {
return <Component user={user} {...props} />; return <Component user={user} {...props} />;
}; };
export default PrivateRoute; export default memo(PrivateRoute);

View File

@ -1,6 +1,6 @@
import cx from 'classnames'; import cx from 'classnames';
import { toUrl } from 'gatsby-source-gravatar'; import { toUrl } from 'gatsby-source-gravatar';
import React, { useContext, useMemo } from 'react'; import React, { memo, useContext, useMemo } from 'react';
import UserContext from '../../contexts/UserContext'; import UserContext from '../../contexts/UserContext';
import styles from './Avatar.module.css'; import styles from './Avatar.module.css';
@ -18,4 +18,4 @@ const Avatar = ({ className }) => {
); );
}; };
export default Avatar; export default memo(Avatar);

View File

@ -1,5 +1,5 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React, { memo } from 'react';
import { handleKeyUp } from '../../utils'; import { handleKeyUp } from '../../utils';
import styles from './Button.module.css'; import styles from './Button.module.css';
@ -21,4 +21,4 @@ const Button = ({ icon, title, onClick, outline, className, isLoading }) => {
); );
}; };
export default Button; export default memo(Button);

View File

@ -1,7 +1,7 @@
import React from 'react'; import React, { memo } from 'react';
const Heading = ({ children }) => { const Heading = ({ children }) => {
return <h2 className="text-4xl">{children}</h2>; return <h2 className="text-4xl">{children}</h2>;
}; };
export default Heading; export default memo(Heading);

View File

@ -1,6 +1,6 @@
import cx from 'classnames'; import cx from 'classnames';
import { get, isFunction } from 'lodash'; import { get, isFunction } from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { memo, useEffect, useState } from 'react';
import { FaAngleDown } from 'react-icons/fa'; import { FaAngleDown } from 'react-icons/fa';
import { MdClose } from 'react-icons/md'; import { MdClose } from 'react-icons/md';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
@ -133,4 +133,4 @@ const Input = ({
); );
}; };
export default Input; export default memo(Input);

View File

@ -0,0 +1,51 @@
import { Field } from 'formik';
import React, { memo } from 'react';
import { MdAdd } from 'react-icons/md';
import { getFieldProps, handleKeyUp } from '../../utils';
import Input from './Input';
const InputArray = ({ formik, schema, helpers, label, path, placeholder }) => {
const handleClickAdd = () => {
formik.values.temp && helpers.push(formik.values.temp);
formik.setFieldValue('temp', '');
};
return (
<div className="col-span-2">
<label>
<span>{label}</span>
{formik.values[path] &&
formik.values[path].map((x, i) => (
<Field key={i} name={`${path}.${i}`}>
{({ field, meta }) => (
<Input
className="my-1"
showDeleteItemButton
onDeleteItem={() => helpers.remove(i)}
{...field}
{...meta}
/>
)}
</Field>
))}
<div className="flex items-center">
<Input
placeholder={placeholder}
{...getFieldProps(formik, schema, 'temp')}
/>
<MdAdd
size="18px"
tabIndex="0"
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
onClick={handleClickAdd}
/>
</div>
</label>
</div>
);
};
export default memo(InputArray);

View File

@ -1,7 +1,7 @@
import cx from 'classnames'; import cx from 'classnames';
import { graphql, useStaticQuery } from 'gatsby'; import { graphql, useStaticQuery } from 'gatsby';
import GatsbyImage from 'gatsby-image'; import GatsbyImage from 'gatsby-image';
import React from 'react'; import React, { memo } from 'react';
import styles from './Logo.module.css'; import styles from './Logo.module.css';
const Logo = ({ size = '256px', className }) => { const Logo = ({ size = '256px', className }) => {
@ -27,4 +27,4 @@ const Logo = ({ size = '256px', className }) => {
); );
}; };
export default Logo; export default memo(Logo);

View File

@ -1,4 +1,4 @@
import React, { useContext, useRef } from 'react'; import React, { memo, useContext, useRef } from 'react';
import { MdFileUpload } from 'react-icons/md'; import { MdFileUpload } from 'react-icons/md';
import StorageContext from '../../contexts/StorageContext'; import StorageContext from '../../contexts/StorageContext';
import { handleKeyUp } from '../../utils'; import { handleKeyUp } from '../../utils';
@ -47,4 +47,4 @@ const PhotoUpload = () => {
); );
}; };
export default PhotoUpload; export default memo(PhotoUpload);

View File

@ -1,5 +1,5 @@
import { Tooltip } from '@material-ui/core'; import { Tooltip } from '@material-ui/core';
import React from 'react'; import React, { memo } from 'react';
import { scroller } from 'react-scroll'; import { scroller } from 'react-scroll';
import { handleKeyUp } from '../../utils'; import { handleKeyUp } from '../../utils';
@ -30,4 +30,4 @@ const SectionIcon = ({ section, containerId, placement = 'right' }) => {
); );
}; };
export default SectionIcon; export default memo(SectionIcon);

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { memo, useEffect } from 'react';
import { Slide, toast } from 'react-toastify'; import { Slide, toast } from 'react-toastify';
import ModalRegistrar from '../../modals/ModalRegistrar'; import ModalRegistrar from '../../modals/ModalRegistrar';
@ -23,4 +23,4 @@ const Wrapper = ({ children }) => {
); );
}; };
export default Wrapper; export default memo(Wrapper);

View File

@ -1,8 +1,16 @@
import firebase from 'gatsby-plugin-firebase'; import firebase from 'gatsby-plugin-firebase';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import React, { createContext, useContext, useEffect, useState } from 'react'; import React, {
createContext,
memo,
useContext,
useEffect,
useState,
} from 'react';
import UserContext from './UserContext'; import UserContext from './UserContext';
const DEBOUNCE_WAIT_TIME = 4000;
const defaultState = { const defaultState = {
isOffline: false, isOffline: false,
isUpdating: false, isUpdating: false,
@ -10,7 +18,8 @@ const defaultState = {
getResumes: async () => {}, getResumes: async () => {},
createResume: () => {}, createResume: () => {},
updateResume: async () => {}, updateResume: async () => {},
debouncedUpdate: async () => {}, debouncedUpdateResume: async () => {},
debouncedUpdateMetadata: async () => {},
deleteResume: () => {}, deleteResume: () => {},
}; };
@ -77,7 +86,23 @@ const DatabaseProvider = ({ children }) => {
setUpdating(false); setUpdating(false);
}; };
const debouncedUpdate = debounce(updateResume, 2000); const debouncedUpdateResume = debounce(updateResume, DEBOUNCE_WAIT_TIME);
const updateMetadata = async (resumeId, metadata) => {
setUpdating(true);
await firebase
.database()
.ref(`users/${user.uid}/resumes/${resumeId}`)
.update({
metadata,
updatedAt: firebase.database.ServerValue.TIMESTAMP,
});
setUpdating(false);
};
const debouncedUpdateMetadata = debounce(updateMetadata, DEBOUNCE_WAIT_TIME);
const deleteResume = (id) => { const deleteResume = (id) => {
firebase.database().ref(`users/${user.uid}/resumes/${id}`).remove(); firebase.database().ref(`users/${user.uid}/resumes/${id}`).remove();
@ -91,7 +116,8 @@ const DatabaseProvider = ({ children }) => {
getResume, getResume,
createResume, createResume,
updateResume, updateResume,
debouncedUpdate, debouncedUpdateResume,
debouncedUpdateMetadata,
deleteResume, deleteResume,
}} }}
> >
@ -102,4 +128,6 @@ const DatabaseProvider = ({ children }) => {
export default DatabaseContext; export default DatabaseContext;
export { DatabaseProvider }; const memoizedProvider = memo(DatabaseProvider);
export { memoizedProvider as DatabaseProvider };

View File

@ -0,0 +1,70 @@
import { clone, setWith } from 'lodash';
import React, {
createContext,
memo,
useCallback,
useContext,
useReducer,
} from 'react';
import leftSections from '../data/leftSections';
import DatabaseContext from './DatabaseContext';
import { useSelector as useResumeSelector } from './ResumeContext';
const initialState = {
template: 'Onyx',
font: 'Montserrat',
layout: [leftSections.map(({ id, name }) => ({ id, name }))],
colors: {
textColor: '#444444',
primaryColor: '#5875DB',
backgroundColor: '#FFFFFF',
},
};
const MetadataContext = createContext({});
const MetadataProvider = ({ children }) => {
const id = useResumeSelector((state) => state.id);
const { debouncedUpdateMetadata } = useContext(DatabaseContext);
const memoizedReducer = useCallback(
(state, { type, payload }) => {
let newState;
switch (type) {
case 'set_layout':
newState = setWith(clone(state), 'layout', payload, clone);
debouncedUpdateMetadata(id, newState);
return newState;
default:
throw new Error();
}
},
[id, debouncedUpdateMetadata],
);
const [state, dispatch] = useReducer(memoizedReducer, initialState);
const selectValue = (callback) => callback(state);
return (
<MetadataContext.Provider value={{ selectValue, dispatch }}>
{children}
</MetadataContext.Provider>
);
};
const useSelector = (callback) => {
const { selectValue } = useContext(MetadataContext);
return selectValue(callback);
};
const useDispatch = () => {
const { dispatch } = useContext(MetadataContext);
return dispatch;
};
const memoizedProvider = memo(MetadataProvider);
export { memoizedProvider as MetadataProvider, useSelector, useDispatch };

View File

@ -1,5 +1,5 @@
import { createNanoEvents } from 'nanoevents'; import { createNanoEvents } from 'nanoevents';
import React, { createContext } from 'react'; import React, { createContext, memo } from 'react';
import ModalEvents from '../constants/ModalEvents'; import ModalEvents from '../constants/ModalEvents';
const emitter = createNanoEvents(); const emitter = createNanoEvents();
@ -18,4 +18,6 @@ const ModalProvider = ({ children }) => {
export default ModalContext; export default ModalContext;
export { ModalProvider }; const memoizedProvider = memo(ModalProvider);
export { memoizedProvider as ModalProvider };

View File

@ -2,6 +2,7 @@ import arrayMove from 'array-move';
import { clone, findIndex, get, setWith } from 'lodash'; import { clone, findIndex, get, setWith } from 'lodash';
import React, { import React, {
createContext, createContext,
memo,
useCallback, useCallback,
useContext, useContext,
useReducer, useReducer,
@ -10,10 +11,10 @@ import DatabaseContext from './DatabaseContext';
const initialState = {}; const initialState = {};
const ResumeContext = createContext(initialState); const ResumeContext = createContext({});
const ResumeProvider = ({ children }) => { const ResumeProvider = ({ children }) => {
const { debouncedUpdate } = useContext(DatabaseContext); const { debouncedUpdateResume } = useContext(DatabaseContext);
const memoizedReducer = useCallback( const memoizedReducer = useCallback(
(state, { type, payload }) => { (state, { type, payload }) => {
@ -31,7 +32,7 @@ const ResumeProvider = ({ children }) => {
[...items, payload.value], [...items, payload.value],
clone, clone,
); );
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'on_edit_item': case 'on_edit_item':
@ -44,7 +45,7 @@ const ResumeProvider = ({ children }) => {
payload.value, payload.value,
clone, clone,
); );
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'on_delete_item': case 'on_delete_item':
@ -52,7 +53,7 @@ const ResumeProvider = ({ children }) => {
index = findIndex(items, ['id', payload.value.id]); index = findIndex(items, ['id', payload.value.id]);
items.splice(index, 1); items.splice(index, 1);
newState = setWith(clone(state), payload.path, items, clone); newState = setWith(clone(state), payload.path, items, clone);
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'on_move_item_up': case 'on_move_item_up':
@ -60,7 +61,7 @@ const ResumeProvider = ({ children }) => {
index = findIndex(items, ['id', payload.value.id]); index = findIndex(items, ['id', payload.value.id]);
items = arrayMove(items, index, index - 1); items = arrayMove(items, index, index - 1);
newState = setWith(clone(state), payload.path, items, clone); newState = setWith(clone(state), payload.path, items, clone);
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'on_move_item_down': case 'on_move_item_down':
@ -68,12 +69,12 @@ const ResumeProvider = ({ children }) => {
index = findIndex(items, ['id', payload.value.id]); index = findIndex(items, ['id', payload.value.id]);
items = arrayMove(items, index, index + 1); items = arrayMove(items, index, index + 1);
newState = setWith(clone(state), payload.path, items, clone); newState = setWith(clone(state), payload.path, items, clone);
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'on_input': case 'on_input':
newState = setWith(clone(state), payload.path, payload.value, clone); newState = setWith(clone(state), payload.path, payload.value, clone);
debouncedUpdate(newState); debouncedUpdateResume(newState);
return newState; return newState;
case 'set_data': case 'set_data':
@ -83,7 +84,7 @@ const ResumeProvider = ({ children }) => {
throw new Error(); throw new Error();
} }
}, },
[debouncedUpdate], [debouncedUpdateResume],
); );
const [state, dispatch] = useReducer(memoizedReducer, initialState); const [state, dispatch] = useReducer(memoizedReducer, initialState);
@ -107,4 +108,6 @@ const useDispatch = () => {
return dispatch; return dispatch;
}; };
export { ResumeProvider, useSelector, useDispatch }; const memoizedProvider = memo(ResumeProvider);
export { memoizedProvider as ResumeProvider, useSelector, useDispatch };

View File

@ -1,5 +1,5 @@
import firebase from 'gatsby-plugin-firebase'; import firebase from 'gatsby-plugin-firebase';
import React, { createContext, useContext, useRef } from 'react'; import React, { createContext, memo, useContext, useRef } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { isFileImage } from '../utils'; import { isFileImage } from '../utils';
import { useDispatch, useSelector } from './ResumeContext'; import { useDispatch, useSelector } from './ResumeContext';
@ -84,4 +84,6 @@ const StorageProvider = ({ children }) => {
export default StorageContext; export default StorageContext;
export { StorageProvider }; const memoizedProvider = memo(StorageProvider);
export { memoizedProvider as StorageProvider };

View File

@ -1,111 +0,0 @@
import { flatten } from 'lodash';
import React, { createContext, useState } from 'react';
import leftSections from '../data/leftSections';
const defaultState = {
selected: 'Onyx',
setSelected: () => {},
colors: {
textColor: '#212121',
primaryColor: '#f44336',
backgroundColor: '#FFFFFF',
},
blocks: [leftSections],
setBlocks: () => {},
setFixedBlocks: () => {},
setSupportedBlocks: () => {},
};
const TemplateContext = createContext(defaultState);
const TemplateProvider = ({ children }) => {
const [selected, setSelected] = useState(defaultState.selected);
const [colors, setColors] = useState(defaultState.colors);
const [blocks, setBlocks] = useState(defaultState.blocks);
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const move = (source, destination, droppableSource, droppableDestination) => {
const sourceClone = Array.from(source);
const destClone = Array.from(destination);
const [removed] = sourceClone.splice(droppableSource.index, 1);
destClone.splice(droppableDestination.index, 0, removed);
const result = {};
result[droppableSource.droppableId] = sourceClone;
result[droppableDestination.droppableId] = destClone;
return result;
};
const onDragEnd = (result) => {
const { source, destination } = result;
if (!destination) {
return;
}
const sInd = +source.droppableId;
const dInd = +destination.droppableId;
if (sInd === dInd) {
const items = reorder(blocks[sInd], source.index, destination.index);
const newState = [...blocks];
newState[sInd] = items;
setBlocks(newState);
} else {
const newResult = move(blocks[sInd], blocks[dInd], source, destination);
const newState = [...blocks];
newState[sInd] = newResult[sInd];
newState[dInd] = newResult[dInd];
setBlocks(newState);
}
};
const setFixedBlocks = (fixedBlocks) => {
const newBlocks = blocks.map((x) =>
x.filter((y) => !fixedBlocks.includes(y)),
);
setBlocks(newBlocks);
};
const setSupportedBlocks = (number) => {
if (number === blocks.length) return;
if (number > blocks.length) {
setBlocks([...blocks, []]);
}
if (number < blocks.length) {
setBlocks([flatten(blocks)]);
}
};
return (
<TemplateContext.Provider
value={{
colors,
blocks,
selected,
setColors,
setBlocks,
onDragEnd,
setSelected,
setFixedBlocks,
setSupportedBlocks,
}}
>
{children}
</TemplateContext.Provider>
);
};
export default TemplateContext;
export { TemplateProvider };

View File

@ -1,4 +1,4 @@
import React, { createContext, useEffect, useState } from 'react'; import React, { createContext, memo, useEffect, useState } from 'react';
const COLOR_CONFIG = { const COLOR_CONFIG = {
light: { light: {
@ -62,4 +62,6 @@ const ThemeProvider = ({ children }) => {
export default ThemeContext; export default ThemeContext;
export { ThemeProvider }; const memoizedProvider = memo(ThemeProvider);
export { memoizedProvider as ThemeProvider };

View File

@ -1,6 +1,6 @@
import firebase from 'gatsby-plugin-firebase'; import firebase from 'gatsby-plugin-firebase';
import { pick } from 'lodash'; import { pick } from 'lodash';
import React, { createContext, useEffect, useState } from 'react'; import React, { createContext, memo, useEffect, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import useAuthState from '../hooks/useAuthState'; import useAuthState from '../hooks/useAuthState';
@ -77,4 +77,6 @@ const UserProvider = ({ children }) => {
export default UserContext; export default UserContext;
export { UserProvider }; const memoizedProvider = memo(UserProvider);
export { memoizedProvider as UserProvider };

View File

@ -7,17 +7,6 @@ import {
IoMdDocument, IoMdDocument,
} from 'react-icons/io'; } from 'react-icons/io';
import { MdPerson, MdSchool, MdTranslate } from 'react-icons/md'; import { MdPerson, MdSchool, MdTranslate } from 'react-icons/md';
import Awards from '../components/builder/sections/Awards';
import Certifications from '../components/builder/sections/Certifications';
import Education from '../components/builder/sections/Education';
import Hobbies from '../components/builder/sections/Hobbies';
import Languages from '../components/builder/sections/Languages';
import Objective from '../components/builder/sections/Objective';
import Profile from '../components/builder/sections/Profile';
import References from '../components/builder/sections/References';
import Skills from '../components/builder/sections/Skills';
import Social from '../components/builder/sections/Social';
import Work from '../components/builder/sections/Work';
import ModalEvents from '../constants/ModalEvents'; import ModalEvents from '../constants/ModalEvents';
export default [ export default [
@ -25,75 +14,64 @@ export default [
id: 'profile', id: 'profile',
name: 'Profile', name: 'Profile',
icon: MdPerson, icon: MdPerson,
component: Profile,
}, },
{ {
id: 'social', id: 'social',
name: 'Social Network', name: 'Social Network',
icon: AiOutlineTwitter, icon: AiOutlineTwitter,
component: Social,
event: ModalEvents.SOCIAL_MODAL, event: ModalEvents.SOCIAL_MODAL,
}, },
{ {
id: 'objective', id: 'objective',
name: 'Objective', name: 'Objective',
icon: IoMdDocument, icon: IoMdDocument,
component: Objective,
}, },
{ {
id: 'work', id: 'work',
name: 'Work Experience', name: 'Work Experience',
icon: IoMdBriefcase, icon: IoMdBriefcase,
component: Work,
event: ModalEvents.WORK_MODAL, event: ModalEvents.WORK_MODAL,
}, },
{ {
id: 'education', id: 'education',
name: 'Education', name: 'Education',
icon: MdSchool, icon: MdSchool,
component: Education,
event: ModalEvents.EDUCATION_MODAL, event: ModalEvents.EDUCATION_MODAL,
}, },
{ {
id: 'awards', id: 'awards',
name: 'Awards', name: 'Awards',
icon: FaAward, icon: FaAward,
component: Awards,
event: ModalEvents.AWARD_MODAL, event: ModalEvents.AWARD_MODAL,
}, },
{ {
id: 'certifications', id: 'certifications',
name: 'Certifications', name: 'Certifications',
icon: AiFillSafetyCertificate, icon: AiFillSafetyCertificate,
component: Certifications,
event: ModalEvents.CERTIFICATION_MODAL, event: ModalEvents.CERTIFICATION_MODAL,
}, },
{ {
id: 'skills', id: 'skills',
name: 'Skills', name: 'Skills',
icon: BsTools, icon: BsTools,
component: Skills,
event: ModalEvents.SKILL_MODAL, event: ModalEvents.SKILL_MODAL,
}, },
{ {
id: 'hobbies', id: 'hobbies',
name: 'Hobbies', name: 'Hobbies',
icon: IoLogoGameControllerB, icon: IoLogoGameControllerB,
component: Hobbies,
event: ModalEvents.HOBBY_MODAL, event: ModalEvents.HOBBY_MODAL,
}, },
{ {
id: 'languages', id: 'languages',
name: 'Languages', name: 'Languages',
icon: MdTranslate, icon: MdTranslate,
component: Languages,
event: ModalEvents.LANGUAGE_MODAL, event: ModalEvents.LANGUAGE_MODAL,
}, },
{ {
id: 'references', id: 'references',
name: 'References', name: 'References',
icon: FaUserFriends, icon: FaUserFriends,
component: References,
event: ModalEvents.REFERENCE_MODAL, event: ModalEvents.REFERENCE_MODAL,
}, },
]; ];

View File

@ -0,0 +1,9 @@
import { MdDashboard } from 'react-icons/md';
export default [
{
id: 'layout',
name: 'Layout',
icon: MdDashboard,
},
];

View File

@ -1,5 +1,5 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import React, { useContext, useEffect, useState } from 'react'; import React, { memo, useContext, useEffect, useState } from 'react';
import Button from '../components/shared/Button'; import Button from '../components/shared/Button';
import ModalContext from '../contexts/ModalContext'; import ModalContext from '../contexts/ModalContext';
import UserContext from '../contexts/UserContext'; import UserContext from '../contexts/UserContext';
@ -63,4 +63,4 @@ const AuthModal = () => {
); );
}; };
export default AuthModal; export default memo(AuthModal);

View File

@ -2,7 +2,7 @@ import Backdrop from '@material-ui/core/Backdrop';
import Fade from '@material-ui/core/Fade'; import Fade from '@material-ui/core/Fade';
import Modal from '@material-ui/core/Modal'; import Modal from '@material-ui/core/Modal';
import { isFunction } from 'lodash'; import { isFunction } from 'lodash';
import React, { forwardRef, useImperativeHandle } from 'react'; import React, { forwardRef, memo, useImperativeHandle } from 'react';
import { MdClose } from 'react-icons/md'; import { MdClose } from 'react-icons/md';
import Button from '../components/shared/Button'; import Button from '../components/shared/Button';
import { handleKeyUp } from '../utils'; import { handleKeyUp } from '../utils';
@ -61,4 +61,4 @@ const BaseModal = forwardRef(
}, },
); );
export default BaseModal; export default memo(BaseModal);

View File

@ -1,6 +1,6 @@
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { isEmpty, isFunction } from 'lodash'; import { isEmpty, isFunction } from 'lodash';
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { memo, useContext, useEffect, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import Button from '../components/shared/Button'; import Button from '../components/shared/Button';
import ModalContext from '../contexts/ModalContext'; import ModalContext from '../contexts/ModalContext';
@ -101,4 +101,4 @@ const DataModal = ({
); );
}; };
export default DataModal; export default memo(DataModal);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import AuthModal from './AuthModal'; import AuthModal from './AuthModal';
import ResumeModal from './ResumeModal'; import ResumeModal from './ResumeModal';
import AwardModal from './sections/AwardModal'; import AwardModal from './sections/AwardModal';
@ -29,4 +29,4 @@ const ModalRegistrar = () => {
); );
}; };
export default ModalRegistrar; export default memo(ModalRegistrar);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../components/shared/Input'; import Input from '../components/shared/Input';
import ModalEvents from '../constants/ModalEvents'; import ModalEvents from '../constants/ModalEvents';
@ -56,4 +56,4 @@ const ResumeModal = () => {
); );
}; };
export default ResumeModal; export default memo(ResumeModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -66,4 +66,4 @@ const AwardModal = () => {
); );
}; };
export default AwardModal; export default memo(AwardModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -66,4 +66,4 @@ const CertificateModal = () => {
); );
}; };
export default CertificateModal; export default memo(CertificateModal);

View File

@ -1,10 +1,10 @@
import { Field, FieldArray, Formik } from 'formik'; import { FieldArray, Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import { MdAdd } from 'react-icons/md';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import InputArray from '../../components/shared/InputArray';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
import { getFieldProps, handleKeyUp } from '../../utils'; import { getFieldProps } from '../../utils';
import DataModal from '../DataModal'; import DataModal from '../DataModal';
const initialValues = { const initialValues = {
@ -90,49 +90,16 @@ const EducationModal = () => {
<FieldArray <FieldArray
name="courses" name="courses"
render={(arrayHelpers) => { render={(helpers) => (
const handleClickAdd = () => { <InputArray
formik.values.temp && arrayHelpers.push(formik.values.temp); formik={formik}
formik.setFieldValue('temp', ''); schema={schema}
}; helpers={helpers}
label="Courses"
return ( path="courses"
<div className="col-span-2"> placeholder="Data Structures &amp; Algortihms"
<label> />
<span>Courses</span> )}
{formik.values.courses &&
formik.values.courses.map((x, i) => (
<Field key={x} name={`courses.${i}`}>
{({ field, meta }) => (
<Input
className="my-1"
showDeleteItemButton
onDeleteItem={() => arrayHelpers.remove(i)}
{...field}
{...meta}
/>
)}
</Field>
))}
<div className="flex items-center">
<Input
placeholder="Algorithms &amp; Data Structures"
{...getFieldProps(formik, schema, 'temp')}
/>
<MdAdd
size="18px"
tabIndex="0"
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
onClick={handleClickAdd}
/>
</div>
</label>
</div>
);
}}
/> />
</div> </div>
</DataModal> </DataModal>
@ -141,4 +108,4 @@ const EducationModal = () => {
); );
}; };
export default EducationModal; export default memo(EducationModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -41,4 +41,4 @@ const HobbyModal = () => {
); );
}; };
export default HobbyModal; export default memo(HobbyModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -48,4 +48,4 @@ const LanguageModal = () => {
); );
}; };
export default LanguageModal; export default memo(LanguageModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -73,4 +73,4 @@ const ReferenceModal = () => {
); );
}; };
export default ReferenceModal; export default memo(ReferenceModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -59,4 +59,4 @@ const SkillModal = () => {
); );
}; };
export default SkillModal; export default memo(SkillModal);

View File

@ -1,5 +1,5 @@
import { Formik } from 'formik'; import { Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
@ -62,4 +62,4 @@ const SocialModal = () => {
); );
}; };
export default SocialModal; export default memo(SocialModal);

View File

@ -1,10 +1,10 @@
import { Field, FieldArray, Formik } from 'formik'; import { FieldArray, Formik } from 'formik';
import React from 'react'; import React, { memo } from 'react';
import { MdAdd } from 'react-icons/md';
import * as Yup from 'yup'; import * as Yup from 'yup';
import Input from '../../components/shared/Input'; import Input from '../../components/shared/Input';
import InputArray from '../../components/shared/InputArray';
import ModalEvents from '../../constants/ModalEvents'; import ModalEvents from '../../constants/ModalEvents';
import { getFieldProps, handleKeyUp } from '../../utils'; import { getFieldProps } from '../../utils';
import DataModal from '../DataModal'; import DataModal from '../DataModal';
const initialValues = { const initialValues = {
@ -92,49 +92,16 @@ const WorkModal = () => {
<FieldArray <FieldArray
name="highlights" name="highlights"
render={(arrayHelpers) => { render={(helpers) => (
const handleClickAdd = () => { <InputArray
formik.values.temp && arrayHelpers.push(formik.values.temp); formik={formik}
formik.setFieldValue('temp', ''); schema={schema}
}; helpers={helpers}
label="Highlights"
return ( path="highlights"
<div className="col-span-2"> placeholder="Worked passionately in customer service in a high volume restaurant."
<label> />
<span>Highlights</span> )}
{formik.values.highlights &&
formik.values.highlights.map((x, i) => (
<Field key={x} name={`highlights.${i}`}>
{({ field, meta }) => (
<Input
className="my-1"
showDeleteItemButton
onDeleteItem={() => arrayHelpers.remove(i)}
{...field}
{...meta}
/>
)}
</Field>
))}
<div className="flex items-center">
<Input
placeholder="Worked passionately in customer service in a high volume restaurant."
{...getFieldProps(formik, schema, 'temp')}
/>
<MdAdd
size="18px"
tabIndex="0"
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
onClick={handleClickAdd}
/>
</div>
</label>
</div>
);
}}
/> />
</div> </div>
</DataModal> </DataModal>
@ -143,4 +110,4 @@ const WorkModal = () => {
); );
}; };
export default WorkModal; export default memo(WorkModal);

View File

@ -1,5 +1,5 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import { useEffect } from 'react'; import { memo, useEffect } from 'react';
const NotFound = () => { const NotFound = () => {
useEffect(() => { useEffect(() => {
@ -9,4 +9,4 @@ const NotFound = () => {
return null; return null;
}; };
export default NotFound; export default memo(NotFound);

View File

@ -1,5 +1,5 @@
import { Redirect, Router } from '@reach/router'; import { Redirect, Router } from '@reach/router';
import React from 'react'; import React, { memo } from 'react';
import PrivateRoute from '../components/router/PrivateRoute'; import PrivateRoute from '../components/router/PrivateRoute';
import Wrapper from '../components/shared/Wrapper'; import Wrapper from '../components/shared/Wrapper';
import NotFound from './404'; import NotFound from './404';
@ -16,4 +16,4 @@ const App = () => (
</Router> </Router>
</Wrapper> </Wrapper>
); );
export default App; export default memo(App);

View File

@ -1,5 +1,5 @@
import { navigate } from 'gatsby'; import { navigate } from 'gatsby';
import React, { useContext, useEffect, useMemo, useState } from 'react'; import React, { memo, useContext, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import Artboard from '../../components/builder/center/Artboard'; import Artboard from '../../components/builder/center/Artboard';
import LeftSidebar from '../../components/builder/left/LeftSidebar'; import LeftSidebar from '../../components/builder/left/LeftSidebar';
@ -51,4 +51,4 @@ const Builder = ({ id }) => {
}, [loading]); }, [loading]);
}; };
export default Builder; export default memo(Builder);

View File

@ -1,5 +1,5 @@
import firebase from 'gatsby-plugin-firebase'; import firebase from 'gatsby-plugin-firebase';
import React, { useEffect, useState } from 'react'; import React, { memo, useEffect, useState } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import CreateResume from '../../components/dashboard/CreateResume'; import CreateResume from '../../components/dashboard/CreateResume';
import ResumePreview from '../../components/dashboard/ResumePreview'; import ResumePreview from '../../components/dashboard/ResumePreview';
@ -58,4 +58,4 @@ const Dashboard = ({ user }) => {
); );
}; };
export default Dashboard; export default memo(Dashboard);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import Hero from '../components/landing/Hero'; import Hero from '../components/landing/Hero';
import Wrapper from '../components/shared/Wrapper'; import Wrapper from '../components/shared/Wrapper';
@ -68,4 +68,4 @@ const Feature = ({ title, children }) => {
); );
}; };
export default Home; export default memo(Home);

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import { FaGlobeAmericas, FaPhone } from 'react-icons/fa'; import { FaGlobeAmericas, FaPhone } from 'react-icons/fa';
import { MdEmail } from 'react-icons/md'; import { MdEmail } from 'react-icons/md';
@ -57,4 +57,4 @@ const Onyx = ({ data, layout, colors }) => {
); );
}; };
export default Onyx; export default memo(Onyx);

View File

@ -31,3 +31,30 @@ export const getFieldProps = (formik, schema, name) => ({
isRequired: get(schema, `fields.${name}._exclusive.required`), isRequired: get(schema, `fields.${name}._exclusive.required`),
...formik.getFieldProps(name), ...formik.getFieldProps(name),
}); });
export const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
export const move = (
source,
destination,
droppableSource,
droppableDestination,
) => {
const sourceClone = Array.from(source);
const destClone = Array.from(destination);
const [removed] = sourceClone.splice(droppableSource.index, 1);
destClone.splice(droppableDestination.index, 0, removed);
const result = {};
result[droppableSource.droppableId] = sourceClone;
result[droppableDestination.droppableId] = destClone;
return result;
};