From 82eb282e179fc3f6ddaaf06f976657f76cd7e9ce Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Fri, 25 Mar 2022 22:45:11 +0600 Subject: [PATCH] Pass config as props --- src/App.jsx | 3 +- src/components/GitProfile.jsx | 112 ++++++++++++-- src/components/avatar-card/index.jsx | 2 +- src/components/blog/index.jsx | 37 ++--- src/components/details/index.jsx | 181 +++++++++++------------ src/components/education/index.jsx | 69 +++++---- src/components/experience/index.jsx | 69 +++++---- src/components/head-tag-editor/index.jsx | 19 ++- src/components/project/index.jsx | 13 +- src/components/skill/index.jsx | 10 +- src/components/theme-changer/index.jsx | 22 ++- src/helpers/utils.jsx | 28 ++-- 12 files changed, 317 insertions(+), 248 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index a8c2e3e..5a777f1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,8 @@ +import config from '../gitprofile.config'; import GitProfile from './components/GitProfile'; function App() { - return ; + return ; } export default App; diff --git a/src/components/GitProfile.jsx b/src/components/GitProfile.jsx index c651e80..e928956 100644 --- a/src/components/GitProfile.jsx +++ b/src/components/GitProfile.jsx @@ -12,12 +12,12 @@ import Education from './education'; import Project from './project'; import Blog from './blog'; import { getInitialTheme, setupHotjar } from '../helpers/utils'; -import config from '../../gitprofile.config'; -import '../assets/index.css'; import { HelmetProvider } from 'react-helmet-async'; +import PropTypes from 'prop-types'; +import '../assets/index.css'; -const GitProfile = () => { - const [theme, setTheme] = useState(getInitialTheme()); +const GitProfile = ({ config }) => { + const [theme, setTheme] = useState(getInitialTheme(config.themeConfig)); const [loading, setLoading] = useState(true); const [profile, setProfile] = useState(null); const [repo, setRepo] = useState(null); @@ -31,7 +31,7 @@ const GitProfile = () => { }, [theme]); useEffect(() => { - setupHotjar(); + setupHotjar(config.hotjar); }, []); const loadData = useCallback(() => { @@ -113,7 +113,12 @@ const GitProfile = () => { return ( - +
{error ? ( { subTitle={ error === 404 ? (

- Please provide correct github username in{' '} - gitprofile.config.js + Please provide correct github username in config

) : error === 429 ? (

@@ -159,19 +163,37 @@ const GitProfile = () => { theme={theme} setTheme={setTheme} loading={loading} + themeConfig={config.themeConfig} /> )} -

- - - +
+ + +
- - + +
@@ -204,4 +226,66 @@ const GitProfile = () => { ); }; +GitProfile.propTypes = { + config: PropTypes.shape({ + github: PropTypes.shape({ + username: PropTypes.string.isRequired, + sortBy: PropTypes.oneOf(['stars', 'updated']).isRequired, + limit: PropTypes.number.isRequired, + exclude: PropTypes.shape({ + forks: PropTypes.bool.isRequired, + projects: PropTypes.array.isRequired, + }).isRequired, + }).isRequired, + social: PropTypes.shape({ + linkedin: PropTypes.string, + twitter: PropTypes.string, + facebook: PropTypes.string, + dribbble: PropTypes.string, + behance: PropTypes.string, + medium: PropTypes.string, + devto: PropTypes.string, + website: PropTypes.string, + phone: PropTypes.string, + email: PropTypes.string, + }).isRequired, + skills: PropTypes.array.isRequired, + experiences: PropTypes.arrayOf( + PropTypes.shape({ + company: PropTypes.string, + position: PropTypes.string, + from: PropTypes.string, + to: PropTypes.string, + }) + ).isRequired, + education: PropTypes.arrayOf( + PropTypes.shape({ + institution: PropTypes.string, + degree: PropTypes.string, + from: PropTypes.string, + to: PropTypes.string, + }) + ).isRequired, + blog: PropTypes.shape({ + source: PropTypes.string, + username: PropTypes.string, + limit: PropTypes.number, + }).isRequired, + googleAnalytics: PropTypes.shape({ + id: PropTypes.string, + }).isRequired, + hotjar: PropTypes.shape({ + id: PropTypes.string, + snippetVersion: PropTypes.number, + }).isRequired, + themeConfig: PropTypes.shape({ + default: PropTypes.string.isRequired, + disableSwitch: PropTypes.bool.isRequired, + respectPrefersColorScheme: PropTypes.bool.isRequired, + themes: PropTypes.array.isRequired, + customTheme: PropTypes.object.isRequired, + }).isRequired, + }).isRequired, +}; + export default GitProfile; diff --git a/src/components/avatar-card/index.jsx b/src/components/avatar-card/index.jsx index bcc94d1..8e8350b 100644 --- a/src/components/avatar-card/index.jsx +++ b/src/components/avatar-card/index.jsx @@ -54,7 +54,7 @@ const AvatarCard = ({ profile, loading }) => { AvatarCard.propTypes = { profile: PropTypes.object, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, }; export default AvatarCard; diff --git a/src/components/blog/index.jsx b/src/components/blog/index.jsx index 7a7175e..a655cdc 100644 --- a/src/components/blog/index.jsx +++ b/src/components/blog/index.jsx @@ -4,15 +4,14 @@ import { Fragment, useEffect, useState } from 'react'; import { ga, skeleton } from '../../helpers/utils'; import LazyImage from '../lazy-image'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; -const displaySection = () => { +const displaySection = (blog) => { if ( - typeof config.blog !== 'undefined' && - typeof config.blog.source !== 'undefined' && - typeof config.blog.username !== 'undefined' && - config.blog.source && - config.blog.username + typeof blog !== 'undefined' && + typeof blog.source !== 'undefined' && + typeof blog.username !== 'undefined' && + blog.source && + blog.username ) { return true; } else { @@ -20,20 +19,20 @@ const displaySection = () => { } }; -const Blog = ({ loading }) => { +const Blog = ({ loading, blog, googleAnalytics }) => { const [articles, setArticles] = useState(null); useEffect(() => { - if (displaySection()) { - if (config.blog.source === 'medium') { + if (displaySection(blog)) { + if (blog.source === 'medium') { getMediumArticle({ - user: config.blog.username, + user: blog.username, }).then((res) => { setArticles(res); }); - } else if (config.blog.source === 'dev.to') { + } else if (blog.source === 'dev.to') { getDevtoArticle({ - user: config.blog.username, + user: blog.username, }).then((res) => { setArticles(res); }); @@ -43,7 +42,7 @@ const Blog = ({ loading }) => { const renderSkeleton = () => { let array = []; - for (let index = 0; index < config.blog.limit; index++) { + for (let index = 0; index < blog.limit; index++) { array.push(
@@ -101,7 +100,7 @@ const Blog = ({ loading }) => { const renderArticles = () => { return ( articles && - articles.slice(0, config.blog.limit).map((article, index) => ( + articles.slice(0, blog.limit).map((article, index) => ( { e.preventDefault(); try { - if (config.googleAnalytics?.id) { + if (googleAnalytics?.id) { ga.event({ action: 'Click Blog Post', params: { @@ -174,7 +173,7 @@ const Blog = ({ loading }) => { return ( - {displaySection() && ( + {displaySection(blog) && (
@@ -207,7 +206,9 @@ const Blog = ({ loading }) => { }; Blog.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + blog: PropTypes.object.isRequired, + googleAnalytics: PropTypes.object.isRequired, }; export default Blog; diff --git a/src/components/details/index.jsx b/src/components/details/index.jsx index e18dcbf..6850ae8 100644 --- a/src/components/details/index.jsx +++ b/src/components/details/index.jsx @@ -14,7 +14,6 @@ import { } from 'react-icons/fa'; import PropTypes from 'prop-types'; import { skeleton } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; const ListItem = ({ icon, title, value, link, skeleton = false }) => { return ( @@ -43,7 +42,7 @@ const ListItem = ({ icon, title, value, link, skeleton = false }) => { ); }; -const Details = ({ profile, loading }) => { +const Details = ({ profile, loading, social, github }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 4; index++) { @@ -86,99 +85,89 @@ const Details = ({ profile, loading }) => { } title="GitHub:" - value={config.github.username} - link={`https://github.com/${config.github.username}`} + value={github.username} + link={`https://github.com/${github.username}`} /> - {typeof config.social.twitter !== 'undefined' && - config.social.twitter && ( - } - title="Twitter:" - value={config.social.twitter} - link={`https://twitter.com/${config.social.twitter}`} - /> - )} - {typeof config.social.linkedin !== 'undefined' && - config.social.linkedin && ( - } - title="LinkedIn:" - value={config.social.linkedin} - link={`https://www.linkedin.com/in/${config.social.linkedin}`} - /> - )} - {typeof config.social.dribbble !== 'undefined' && - config.social.dribbble && ( - } - title="Dribbble:" - value={config.social.dribbble} - link={`https://dribbble.com/${config.social.dribbble}`} - /> - )} - {typeof config.social.behance !== 'undefined' && - config.social.behance && ( - } - title="Behance:" - value={config.social.behance} - link={`https://www.behance.net/${config.social.behance}`} - /> - )} - {typeof config.social.facebook !== 'undefined' && - config.social.facebook && ( - } - title="Facebook:" - value={config.social.facebook} - link={`https://www.facebook.com/${config.social.facebook}`} - /> - )} - {typeof config.social.medium !== 'undefined' && - config.social.medium && ( - } - title="Medium:" - value={config.social.medium} - link={`https://medium.com/@${config.social.medium}`} - /> - )} - {typeof config.social.devto !== 'undefined' && - config.social.devto && ( - } - title="Dev:" - value={config.social.devto} - link={`https://dev.to/${config.social.devto}`} - /> - )} - {typeof config.social.website !== 'undefined' && - config.social.website && ( - } - title="Website:" - value={config.social.website} - link={config.social.website} - /> - )} - {typeof config.social.phone !== 'undefined' && - config.social.phone && ( - } - title="Phone:" - value={config.social.phone} - link={`tel:${config.social.phone}`} - /> - )} - {typeof config.social.email !== 'undefined' && - config.social.email && ( - } - title="Email:" - value={config.social.email} - link={`mailto:${config.social.email}`} - /> - )} + {typeof social.twitter !== 'undefined' && social.twitter && ( + } + title="Twitter:" + value={social.twitter} + link={`https://twitter.com/${social.twitter}`} + /> + )} + {typeof social.linkedin !== 'undefined' && social.linkedin && ( + } + title="LinkedIn:" + value={social.linkedin} + link={`https://www.linkedin.com/in/${social.linkedin}`} + /> + )} + {typeof social.dribbble !== 'undefined' && social.dribbble && ( + } + title="Dribbble:" + value={social.dribbble} + link={`https://dribbble.com/${social.dribbble}`} + /> + )} + {typeof social.behance !== 'undefined' && social.behance && ( + } + title="Behance:" + value={social.behance} + link={`https://www.behance.net/${social.behance}`} + /> + )} + {typeof social.facebook !== 'undefined' && social.facebook && ( + } + title="Facebook:" + value={social.facebook} + link={`https://www.facebook.com/${social.facebook}`} + /> + )} + {typeof social.medium !== 'undefined' && social.medium && ( + } + title="Medium:" + value={social.medium} + link={`https://medium.com/@${social.medium}`} + /> + )} + {typeof social.devto !== 'undefined' && social.devto && ( + } + title="Dev:" + value={social.devto} + link={`https://dev.to/${social.devto}`} + /> + )} + {typeof social.website !== 'undefined' && social.website && ( + } + title="Website:" + value={social.website} + link={social.website} + /> + )} + {typeof social.phone !== 'undefined' && social.phone && ( + } + title="Phone:" + value={social.phone} + link={`tel:${social.phone}`} + /> + )} + {typeof social.email !== 'undefined' && social.email && ( + } + title="Email:" + value={social.email} + link={`mailto:${social.email}`} + /> + )} )}
@@ -189,7 +178,9 @@ const Details = ({ profile, loading }) => { Details.propTypes = { profile: PropTypes.object, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + social: PropTypes.object.isRequired, + github: PropTypes.object.isRequired, }; ListItem.propTypes = { diff --git a/src/components/education/index.jsx b/src/components/education/index.jsx index e817c4c..e959df7 100644 --- a/src/components/education/index.jsx +++ b/src/components/education/index.jsx @@ -1,7 +1,6 @@ import { skeleton } from '../../helpers/utils'; import { Fragment } from 'react'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; const ListItem = ({ time, degree, institution }) => (
  • @@ -15,7 +14,7 @@ const ListItem = ({ time, degree, institution }) => (
  • ); -const Education = ({ loading }) => { +const Education = ({ loading, education }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 2; index++) { @@ -41,46 +40,46 @@ const Education = ({ loading }) => { return ( <> - {typeof config.education !== 'undefined' && - config.education.length !== 0 && ( -
    -
    -
    -
    - {loading ? ( - skeleton({ width: 'w-32', height: 'h-8' }) - ) : ( - Education - )} -
    -
    -
    -
      - {loading ? ( - renderSkeleton() - ) : ( - - {config.education.map((item, index) => ( - - ))} - - )} -
    -
    + {typeof education !== 'undefined' && education.length !== 0 && ( +
    +
    +
    +
    + {loading ? ( + skeleton({ width: 'w-32', height: 'h-8' }) + ) : ( + Education + )} +
    +
    +
    +
      + {loading ? ( + renderSkeleton() + ) : ( + + {education.map((item, index) => ( + + ))} + + )} +
    - )} +
    + )} ); }; Education.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + education: PropTypes.array.isRequired, }; ListItem.propTypes = { diff --git a/src/components/experience/index.jsx b/src/components/experience/index.jsx index 9ab1b19..c3ad474 100644 --- a/src/components/experience/index.jsx +++ b/src/components/experience/index.jsx @@ -1,7 +1,6 @@ import { skeleton } from '../../helpers/utils'; import { Fragment } from 'react'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; const ListItem = ({ time, position, company }) => (
  • @@ -15,7 +14,7 @@ const ListItem = ({ time, position, company }) => (
  • ); -const Experience = ({ loading }) => { +const Experience = ({ experiences, loading }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 2; index++) { @@ -41,40 +40,39 @@ const Experience = ({ loading }) => { return ( <> - {typeof config.experiences !== 'undefined' && - config.experiences.length !== 0 && ( -
    -
    -
    -
    - {loading ? ( - skeleton({ width: 'w-32', height: 'h-8' }) - ) : ( - Experience - )} -
    -
    -
    -
      - {loading ? ( - renderSkeleton() - ) : ( - - {config.experiences.map((experience, index) => ( - - ))} - - )} -
    -
    + {typeof experiences !== 'undefined' && experiences.length !== 0 && ( +
    +
    +
    +
    + {loading ? ( + skeleton({ width: 'w-32', height: 'h-8' }) + ) : ( + Experience + )} +
    +
    +
    +
      + {loading ? ( + renderSkeleton() + ) : ( + + {experiences.map((experience, index) => ( + + ))} + + )} +
    - )} +
    + )} ); }; @@ -86,7 +84,8 @@ ListItem.propTypes = { }; Experience.propTypes = { - loading: PropTypes.bool, + experiences: PropTypes.array.isRequired, + loading: PropTypes.bool.isRequired, }; export default Experience; diff --git a/src/components/head-tag-editor/index.jsx b/src/components/head-tag-editor/index.jsx index fa3320a..bfab1ef 100644 --- a/src/components/head-tag-editor/index.jsx +++ b/src/components/head-tag-editor/index.jsx @@ -2,25 +2,24 @@ import { Fragment } from 'react'; import { Helmet } from 'react-helmet-async'; import PropTypes from 'prop-types'; import { isThemeDarkish } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; -const HeadTagEditor = ({ profile, theme }) => { +const HeadTagEditor = ({ profile, theme, googleAnalytics, social }) => { return ( {profile && ( - {config.googleAnalytics?.id && ( + {googleAnalytics?.id && ( )} - {config.googleAnalytics?.id && ( + {googleAnalytics?.id && ( )} Portfolio of {profile.name} @@ -38,9 +37,7 @@ const HeadTagEditor = ({ profile, theme }) => { @@ -60,7 +57,9 @@ const HeadTagEditor = ({ profile, theme }) => { HeadTagEditor.propTypes = { profile: PropTypes.object, - theme: PropTypes.string, + theme: PropTypes.string.isRequired, + googleAnalytics: PropTypes.object.isRequired, + social: PropTypes.object.isRequired, }; export default HeadTagEditor; diff --git a/src/components/project/index.jsx b/src/components/project/index.jsx index 6c67c97..3f9397d 100644 --- a/src/components/project/index.jsx +++ b/src/components/project/index.jsx @@ -2,12 +2,11 @@ import { Fragment } from 'react'; import { AiOutlineStar, AiOutlineFork } from 'react-icons/ai'; import PropTypes from 'prop-types'; import { ga, languageColor, skeleton } from '../../helpers/utils'; -import config from '../../../gitprofile.config'; -const Project = ({ repo, loading }) => { +const Project = ({ repo, loading, github, googleAnalytics }) => { const renderSkeleton = () => { let array = []; - for (let index = 0; index < config.github.limit; index++) { + for (let index = 0; index < github.limit; index++) { array.push(
    @@ -61,7 +60,7 @@ const Project = ({ repo, loading }) => { e.preventDefault(); try { - if (config.googleAnalytics?.id) { + if (googleAnalytics?.id) { ga.event({ action: 'Click project', params: { @@ -145,7 +144,7 @@ const Project = ({ repo, loading }) => { skeleton({ width: 'w-10', height: 'h-5' }) ) : ( { Project.propTypes = { repo: PropTypes.array, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + github: PropTypes.object.isRequired, + googleAnalytics: PropTypes.object.isRequired, }; export default Project; diff --git a/src/components/skill/index.jsx b/src/components/skill/index.jsx index f51f83b..3ed90f9 100644 --- a/src/components/skill/index.jsx +++ b/src/components/skill/index.jsx @@ -1,8 +1,7 @@ import { skeleton } from '../../helpers/utils'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; -const Skill = ({ loading }) => { +const Skill = ({ loading, skills }) => { const renderSkeleton = () => { let array = []; for (let index = 0; index < 12; index++) { @@ -18,7 +17,7 @@ const Skill = ({ loading }) => { return ( <> - {typeof config.skills !== 'undefined' && config.skills.length !== 0 && ( + {typeof skills !== 'undefined' && skills.length !== 0 && (
    @@ -34,7 +33,7 @@ const Skill = ({ loading }) => {
    {loading ? renderSkeleton() - : config.skills.map((skill, index) => ( + : skills.map((skill, index) => (
    { }; Skill.propTypes = { - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, + skills: PropTypes.array.isRequired, }; export default Skill; diff --git a/src/components/theme-changer/index.jsx b/src/components/theme-changer/index.jsx index 77f86a7..00ed5f1 100644 --- a/src/components/theme-changer/index.jsx +++ b/src/components/theme-changer/index.jsx @@ -1,9 +1,8 @@ import { AiOutlineControl } from 'react-icons/ai'; import { skeleton } from '../../helpers/utils'; import PropTypes from 'prop-types'; -import config from '../../../gitprofile.config'; -const ThemeChanger = ({ theme, setTheme, loading }) => { +const ThemeChanger = ({ theme, setTheme, loading, themeConfig }) => { const changeTheme = (e, selectedTheme) => { e.preventDefault(); document.querySelector('html').setAttribute('data-theme', selectedTheme); @@ -28,7 +27,7 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { {loading ? skeleton({ width: 'w-16', height: 'h-5' }) - : theme === config.themeConfig.default + : theme === themeConfig.default ? 'Default' : theme} @@ -62,9 +61,9 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { >
      {[ - config.themeConfig.default, - ...config.themeConfig.themes.filter( - (item) => item !== config.themeConfig.default + themeConfig.default, + ...themeConfig.themes.filter( + (item) => item !== themeConfig.default ), ].map((item, index) => (
    • @@ -74,9 +73,7 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { className={`${theme === item ? 'active' : ''}`} > - {item === config.themeConfig.default - ? 'Default' - : item} + {item === themeConfig.default ? 'Default' : item}
    • @@ -92,9 +89,10 @@ const ThemeChanger = ({ theme, setTheme, loading }) => { }; ThemeChanger.propTypes = { - theme: PropTypes.string, - setTheme: PropTypes.func, - loading: PropTypes.bool, + theme: PropTypes.string.isRequired, + setTheme: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + themeConfig: PropTypes.object.isRequired, }; export default ThemeChanger; diff --git a/src/helpers/utils.jsx b/src/helpers/utils.jsx index d13f7f0..19cb17a 100644 --- a/src/helpers/utils.jsx +++ b/src/helpers/utils.jsx @@ -1,32 +1,28 @@ import colors from '../data/colors.json'; import { hotjar } from 'react-hotjar'; -import config from '../../gitprofile.config'; -export const getInitialTheme = () => { - if (config.themeConfig.disableSwitch) { - return config.themeConfig.default; +export const getInitialTheme = (themeConfig) => { + if (themeConfig.disableSwitch) { + return themeConfig.default; } if ( typeof window !== 'undefined' && !(localStorage.getItem('gitprofile-theme') === null) && - config.themeConfig.themes.includes(localStorage.getItem('gitprofile-theme')) + themeConfig.themes.includes(localStorage.getItem('gitprofile-theme')) ) { let theme = localStorage.getItem('gitprofile-theme'); return theme; } - if ( - config.themeConfig.respectPrefersColorScheme && - !config.themeConfig.disableSwitch - ) { + if (themeConfig.respectPrefersColorScheme && !themeConfig.disableSwitch) { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' - : config.themeConfig.default; + : themeConfig.default; } - return config.themeConfig.default; + return themeConfig.default; }; export const skeleton = ({ @@ -92,12 +88,12 @@ export const isThemeDarkish = (theme) => { } }; -export const setupHotjar = () => { - if (config.hotjar?.id) { - let snippetVersion = config.hotjar?.snippetVersion - ? config.hotjar?.snippetVersion +export const setupHotjar = (hotjarConfig) => { + if (hotjarConfig?.id) { + let snippetVersion = hotjarConfig?.snippetVersion + ? hotjarConfig?.snippetVersion : 6; - hotjar.initialize(config.hotjar.id, snippetVersion); + hotjar.initialize(hotjarConfig.id, snippetVersion); } };