├── .gitignore
├── gatsby-browser.js
├── assets
├── blog1.png
├── gatsby.png
├── graphql.png
├── blog-main.png
├── contentful.png
├── cover_page.jpg
├── firebase.png
├── material-ui.png
├── react.svg
└── netlify.svg
├── .vscode
└── settings.json
├── src
├── components
│ ├── modal
│ │ ├── modal.css
│ │ └── modal.tsx
│ ├── Header
│ │ ├── header.css
│ │ └── header.tsx
│ ├── Footer
│ │ ├── footer.css
│ │ └── footer.tsx
│ ├── Layout
│ │ └── Layout.tsx
│ ├── BlogList
│ │ ├── blogList.css
│ │ └── blogList.tsx
│ └── Blog
│ │ ├── blogs.css
│ │ └── blogs.tsx
├── style
│ ├── global.css
│ └── modules
│ │ └── main.module.css
├── context
│ ├── modal
│ │ ├── modalReducer.tsx
│ │ └── modal.tsx
│ └── auth
│ │ ├── authReducer.js
│ │ └── auth.js
├── firebase
│ └── firebaseConfig.js
└── pages
│ ├── about
│ ├── about.css
│ └── index.tsx
│ └── index.tsx
├── LICENSE
├── data.tsx
├── gatsby-config.js
├── package.json
├── gatsby-node.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | node_modules
3 | public
4 | .env
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | import "./src/style/global.css";
2 |
--------------------------------------------------------------------------------
/assets/blog1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/blog1.png
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "compile-hero.disable-compile-files-on-did-save-code": true
3 | }
--------------------------------------------------------------------------------
/assets/gatsby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/gatsby.png
--------------------------------------------------------------------------------
/assets/graphql.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/graphql.png
--------------------------------------------------------------------------------
/assets/blog-main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/blog-main.png
--------------------------------------------------------------------------------
/assets/contentful.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/contentful.png
--------------------------------------------------------------------------------
/assets/cover_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/cover_page.jpg
--------------------------------------------------------------------------------
/assets/firebase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/firebase.png
--------------------------------------------------------------------------------
/src/components/modal/modal.css:
--------------------------------------------------------------------------------
1 | .google{
2 | height: 36px;
3 | margin-right: 10px;
4 | }
5 |
--------------------------------------------------------------------------------
/assets/material-ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hinakhadim/blog-in-gatsby/HEAD/assets/material-ui.png
--------------------------------------------------------------------------------
/src/style/global.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Karla&display=swap');
2 |
3 | body {
4 | margin: 0;
5 | font-family: 'Karla', sans-serif;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Header/header.css:
--------------------------------------------------------------------------------
1 | .image-Container{
2 | position: relative;
3 | }
4 |
5 | .image-Container::after{
6 | content: '';
7 | position: absolute;
8 | top:0;
9 | left: 0;
10 | width: 100%;
11 | height: 600px;
12 | background-color: rgba(0,0,0,0.4);
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/Footer/footer.css:
--------------------------------------------------------------------------------
1 |
2 | .footer > p{
3 | font-size: 12px !important;
4 | padding: 40px 0;
5 | color: #2E4053 !important;
6 | margin:0 !important;
7 | }
8 |
9 | .color{
10 | color:#2471A3;
11 | font-weight: bold;
12 | }
13 |
14 | .center{
15 | text-align: center;
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/Layout/Layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Header from "../Header/header";
3 | import { Footer} from '../Footer/footer';
4 |
5 | export default function Layout({ children }) {
6 | return (
7 |
8 |
9 | {children}
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/context/modal/modalReducer.tsx:
--------------------------------------------------------------------------------
1 | export const modalReducer = (state, action) => {
2 | switch (action.type) {
3 | case 'OPEN_MODAL':
4 | return {
5 | ...state,
6 | open: true
7 | }
8 | case 'CLOSE_MODAL':
9 | return {
10 | ...state,
11 | open:false
12 | }
13 | default:
14 | return state;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/firebase/firebaseConfig.js:
--------------------------------------------------------------------------------
1 | import firebase from "firebase";
2 | import "firebase/app";
3 | import "firebase/auth";
4 |
5 | const firebaseConfig = {
6 | apiKey: process.env.GATSBY_FIREBASE_API_KEY,
7 | authDomain: process.env.GATSBY_FIREBASE_AUTH_DOMAIN,
8 | databaseURL: process.env.GATSBY_FIREBASE_AUTH_DATABASE_URL,
9 | projectId: process.env.GATSBY_FIREBASE_PROJECT_ID,
10 | storageBucket: process.env.GATSBY_FIREBASE_STORAGE_BUCKET,
11 | messagingSenderId: process.env.GATSBY_FIREBASE_MESSENGING_SENDER_ID,
12 | appId: process.env.GATSBY_FIREBASE_APP_ID,
13 | };
14 |
15 | firebase.initializeApp(firebaseConfig);
16 | firebase.auth();
17 |
18 | export default { firebaseConfig };
19 |
--------------------------------------------------------------------------------
/src/components/Footer/footer.tsx:
--------------------------------------------------------------------------------
1 | import React, { PropsWithChildren } from 'react';
2 | import './footer.css';
3 | import { data } from '../../../data';
4 |
5 |
6 | interface Props extends PropsWithChildren {
7 | center ?: boolean
8 | }
9 |
10 | export function Footer({ center=false}:Props) {
11 | return (
12 |
13 |
14 |
15 | BLOGS
16 | {" "}© {new Date().getFullYear()}. POWERED BY{" "}
17 | {data.name.split(' ')[0]}.
18 |
19 | );
20 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The BSD Zero Clause License (0BSD)
2 |
3 | Copyright (c) 2020 Gatsby Inc.
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 | PERFORMANCE OF THIS SOFTWARE.
15 |
--------------------------------------------------------------------------------
/data.tsx:
--------------------------------------------------------------------------------
1 | interface Info{
2 | name: String,
3 | introduction: String,
4 | description: String,
5 | linkedIn: String,
6 | github: String,
7 | twitter: String,
8 | mail: String,
9 | facebook : String,
10 | }
11 |
12 | export const data: Info = {
13 | name: "HINA KHADIM",
14 | introduction: "Undergraduate Software Engineer | Learner | Mentor",
15 | description:
16 | "A Full Stack Web developer trying to make the world better place through coding 😇. Loves to code in Python and Javascript.💜 ",
17 | linkedIn: "https://www.linkedin.com/in/hina-khadim-632845178/",
18 | github: "https://github.com/Hina-softwareEngineer",
19 | twitter: "http://twitter.com/hinaKhadim_2002",
20 | mail: "hinakhadim2002@gmail.com",
21 | facebook: "https://www.facebook.com/hina.hina.35574406",
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/BlogList/blogList.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Crimson+Text&display=swap');
2 | .blog-list{
3 | max-width: 900px;
4 | margin: 80px auto;
5 | font-family: 'Karla', sans-serif;
6 | }
7 |
8 | .blog-list > h1{
9 | font-size: 2.5em;
10 | margin-bottom: 10px;
11 | font-family: 'Crimson Text', serif;
12 | font-weight: 500;
13 | }
14 |
15 | @media screen and (max-width: 600px){
16 | .blog-list > h1{
17 | margin-left: 15px;
18 | }
19 | }
20 |
21 | .divider{
22 | width: 100%;
23 | height: 1px;
24 | background-color: #69A297;
25 | margin-bottom: 100px;
26 | }
27 |
28 | h2{
29 | font-size: 2rem !important;
30 | font-weight: 400 !important;
31 | color: #000000;
32 | text-decoration: none;
33 | font-family: 'Crimson Text', serif !important;
34 | cursor : pointer;
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/context/modal/modal.tsx:
--------------------------------------------------------------------------------
1 | import React, { createContext,useReducer,createRef } from "react";
2 | import { modalReducer } from './modalReducer';
3 |
4 | export const ModalContext = createContext(null);
5 |
6 | let initialState = {
7 | open: false
8 | };
9 |
10 | export const ModalContextProvider = (props) => {
11 | let [openLoginModal, dispatch] = useReducer(modalReducer, initialState);
12 |
13 | let blogsHeadingRef = createRef();
14 |
15 | const handleOpenLoginModal = () => {
16 | dispatch({
17 | type:"OPEN_MODAL"
18 | });
19 | };
20 |
21 | const handleCloseLoginModal = () => {
22 | dispatch({
23 | type:"CLOSE_MODAL"
24 | });
25 | };
26 |
27 | return (
28 |
32 | {props.children}
33 |
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config({
2 | path: `.env`,
3 | });
4 |
5 | module.exports = {
6 | siteMetadata: {
7 | title: `HINA KHADIM BLOGS`,
8 | description: `Hey folks, Are you interested in knowing how to become
9 | a professional Software Engineer or do you want to know my Coding Journey?...`,
10 | },
11 | plugins: [
12 | "gatsby-plugin-typescript",
13 | `gatsby-plugin-material-ui`,
14 | {
15 | resolve: `gatsby-transformer-remark`,
16 | options: {
17 | plugins: [
18 | {
19 | resolve: `gatsby-remark-prismjs`,
20 | options: {
21 | aliases: { sh: "bash", js: "javascript" },
22 | showLineNumbers: true,
23 | },
24 | },
25 | ],
26 | },
27 | },
28 | {
29 | resolve: `gatsby-source-contentful`,
30 | options: {
31 | spaceId: process.env.SPACE_ID,
32 | accessToken: process.env.ACCESS_TOKEN,
33 | forceFullSync: true,
34 | },
35 | },
36 | ],
37 | };
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-minimal",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "A Gatsby starter for running recipes and adding themes. It does not come with any pages.",
6 | "author": "Laurie Barth",
7 | "keywords": [
8 | "gatsby"
9 | ],
10 | "scripts": {
11 | "develop": "gatsby develop",
12 | "start": "gatsby develop",
13 | "build": "gatsby build",
14 | "clean": "gatsby clean"
15 | },
16 | "license": "0BSD",
17 | "dependencies": {
18 | "@contentful/rich-text-react-renderer": "^14.1.1",
19 | "@material-ui/core": "^4.11.0",
20 | "@material-ui/icons": "^4.9.1",
21 | "@material-ui/lab": "^4.0.0-alpha.56",
22 | "@material-ui/styles": "^4.10.0",
23 | "dotenv": "^8.2.0",
24 | "firebase": "^7.23.0",
25 | "gatsby": "^2.24.63",
26 | "gatsby-plugin-material-ui": "^2.1.10",
27 | "gatsby-remark-prismjs": "^3.5.16",
28 | "gatsby-source-contentful": "^2.3.49",
29 | "gatsby-transformer-remark": "^2.8.38",
30 | "mdi-material-ui": "^6.19.0",
31 | "prismjs": "^1.22.0",
32 | "react": "^16.13.1",
33 | "react-dom": "^16.13.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/pages/about/about.css:
--------------------------------------------------------------------------------
1 | .info-section{
2 | max-width: 800px;
3 | margin: 20px auto;
4 | margin-top:115px;
5 | text-align: center;
6 | padding: 20px;
7 | background-color: #f6f6f6;
8 | box-shadow: 0 2px 4px lightgrey;
9 | }
10 |
11 | .info-section > h1{
12 | font-family: 'Crimson Text', serif;
13 | font-size: 44px;
14 | }
15 |
16 | .info-section > h3{
17 | font-family: 'Crimson Text', serif;
18 | font-size: 30px;
19 | margin: 0;
20 | }
21 |
22 | .info-section > p{
23 | font-size: 18px;
24 | letter-spacing: 0.7px;
25 | }
26 |
27 | .social-links{
28 | display: flex;
29 | justify-content: center;
30 | align-items: center;
31 | margin: 40px 0;
32 | }
33 |
34 | .social-links > li{
35 | list-style-type: none;
36 | }
37 |
38 | .social-links > li > a{
39 | margin: 0 10px;
40 | }
41 |
42 | .social-links > li > a > svg{
43 | font-size: 2.5em;
44 | }
45 |
46 | .linkedin{color: #0073b1;}
47 |
48 | .github{color: #24292e}
49 |
50 | .twitter{ color: #1da1f2}
51 |
52 | .mail{color: #D44638}
53 |
54 | .facebook{color: #1877f2}
55 |
56 | @media screen and (max-width: 700px){
57 | .info-section{
58 | margin-left: 15px;
59 | margin-right: 15px;
60 | }
61 | }
--------------------------------------------------------------------------------
/src/context/auth/authReducer.js:
--------------------------------------------------------------------------------
1 | export const authReducer = (state, action) => {
2 | switch (action.type) {
3 | case "LOADED":
4 | return {
5 | ...state,
6 | isLoading: true,
7 | isAuthenticated: false,
8 | };
9 | case "LOADING":
10 | return {
11 | ...state,
12 | ...action.payload,
13 | isLoading: false,
14 | isAuthenticated: true,
15 | user: action.payload,
16 | error: null,
17 | };
18 | case "LOGIN_SUCCESSFULLY":
19 | localStorage.setItem("token", action.payload.accessToken);
20 | return {
21 | ...state,
22 | isLoading: false,
23 | isAuthenticated: true,
24 | user: action.payload.email,
25 | error: null,
26 | };
27 | case "LOGIN_FAILED":
28 | localStorage.removeItem("token");
29 | return {
30 | ...state,
31 | isLoading: false,
32 | isAuthenticated: false,
33 | user: null,
34 | error: action.payload.message,
35 | };
36 | case "SIGN_OUT":
37 | localStorage.removeItem("token");
38 | return {
39 | ...state,
40 | isLoading: false,
41 | isAuthenticated: false,
42 | user: null,
43 | error: null,
44 | };
45 | default:
46 | return state;
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/src/style/modules/main.module.css:
--------------------------------------------------------------------------------
1 |
2 | .aboutMe{
3 | margin: 10% auto !important;
4 | text-align: center;
5 | padding: 20px;
6 | max-width: 600px;
7 | }
8 |
9 | .h3About{
10 | margin: 0;
11 | color: #fff;
12 | font-family: 'Karla', sans-serif;
13 | text-shadow: 0 2px 4px #000;
14 | }
15 |
16 | .infoAbout{
17 | margin: 0 ;
18 | font-size: 18px;
19 | color: #fff;
20 | font-family: 'Karla', sans-serif;
21 | text-shadow: 0 2px 4px #000;
22 | }
23 |
24 | .intro{
25 | position: absolute;
26 | top:0;
27 | left:0;
28 | width: 100%;
29 | height: 100%;
30 | z-index:10;
31 | }
32 |
33 | .aboutName{
34 | margin: 0;
35 | text-shadow: 0 2px 4px #000;
36 | font-family: 'Karla', sans-serif;
37 | color: #fff;
38 | }
39 |
40 |
41 | .hand{
42 | display: inline-block;
43 | text-shadow:0 1px 2px #000;
44 | animation: rotate-anim 2s linear 1s infinite forwards;
45 | }
46 |
47 | @keyframes rotate-anim{
48 | 0%{
49 | transform: rotate(0deg);
50 | }
51 | 10%{
52 | transform: rotate(-20deg);
53 | }
54 | 20%{
55 | transform: rotate(20deg);
56 | }
57 | 30%{
58 | transform: rotate(-5deg);
59 | }
60 | 40%{
61 | transform: rotate(20deg);
62 | }
63 | 50%{
64 | transform: rotate(0deg);
65 | }
66 | 100%{
67 | transform: rotate(0deg);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
2 | if (stage === "build-html") {
3 | actions.setWebpackConfig({
4 | externals: getConfig().externals.concat(function (
5 | context,
6 | request,
7 | callback
8 | ) {
9 | const regex = /^@?firebase(\/(.+))?/;
10 | if (regex.test(request)) {
11 | return callback(null, `umd ${request}`);
12 | }
13 | callback();
14 | }),
15 | });
16 | }
17 | };
18 |
19 | exports.createPages = async function ({ graphql, actions }) {
20 | const { createPage } = actions;
21 | const response = await graphql(`
22 | query {
23 | allContentfulBlogModel {
24 | nodes {
25 | title
26 | slug
27 | publishedDate(formatString: "YYYY MMM, DD")
28 | featuredImage {
29 | fluid {
30 | src
31 | }
32 | }
33 | body {
34 | json
35 | }
36 | excerpt {
37 | excerpt
38 | }
39 | }
40 | }
41 | }
42 | `);
43 |
44 | response.data.allContentfulBlogModel.nodes.forEach((edge) => {
45 | createPage({
46 | path: `/blog/${edge.slug}`,
47 | component: require.resolve("./src/components/Blog/blogs.tsx"),
48 | context: {
49 | title: edge.title,
50 | slug: edge.slug,
51 | publishedDate: edge.publishedDate,
52 | featuredImage: edge.featuredImage.fluid.src,
53 | body: edge.body.json,
54 | excerpt: edge,
55 | },
56 | });
57 | });
58 | };
59 |
--------------------------------------------------------------------------------
/src/pages/about/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link} from 'gatsby';
3 |
4 | import { GlobalAuthProvider } from "../../context/auth/auth";
5 | import { ModalContextProvider } from "../../context/modal/modal";
6 |
7 | import Header from '../../components/Header/header';
8 | import { data } from '../../../data';
9 |
10 | import FacebookIcon from '@material-ui/icons/Facebook';
11 | import TwitterIcon from '@material-ui/icons/Twitter';
12 | import GitHubIcon from '@material-ui/icons/GitHub';
13 | import EmailIcon from '@material-ui/icons/Email';
14 | import LinkedInIcon from '@material-ui/icons/LinkedIn';
15 | import './about.css';
16 |
17 | export default function About() {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
About Me
25 |
I'm {data.name}
26 |
{data.introduction}
27 |
28 | {
29 | data.description
30 | }
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/Blog/blogs.css:
--------------------------------------------------------------------------------
1 | .main-container{
2 | max-width: 950px;
3 | margin: 10px auto;
4 | margin-top:64px;
5 | }
6 |
7 | .images-inside-container{
8 | max-width: 800px;
9 | width: 100%;
10 | height: 350px;
11 | /* border:2px solid #000; */
12 | }
13 |
14 | .main-blog{
15 | max-width: 800px;
16 | margin: 0 auto;
17 | padding: 0 10px;
18 | }
19 |
20 |
21 | .main-blog > h1{
22 | font-family: 'Crimson Text', serif;
23 | font-size: 44px;
24 | font-weight: 600;
25 | line-height: 1.2;
26 | }
27 |
28 | .main-blog > .date{
29 | font-size: 14px;
30 | color: #aaa;
31 | letter-spacing: 1px;
32 | margin-bottom: 40px;
33 | }
34 |
35 | .main-blog >div> p{
36 | font-family: 'Karla', sans-serif;
37 | font-size: 18px;
38 | color: #666;
39 | line-height: 1.6;
40 | margin-bottom: 40px;
41 | }
42 |
43 | .post-content{
44 | margin: 80px 0;
45 | }
46 |
47 | .post-content > p:first-child::first-letter{
48 | font-size: 60px;
49 | font-weight: 500;
50 | float: left;
51 | margin-right: 15px;
52 | position: relative;
53 | text-transform: uppercase;
54 | vertical-align: bottom;
55 | color: #000;
56 | line-height: 0.8;
57 | }
58 |
59 | pre{
60 | background:#000;
61 | padding: 25px;
62 | border-radius: 3px;
63 | overflow-x: scroll;
64 | }
65 |
66 | pre > code{
67 | color : #fff;
68 | }
69 |
70 |
71 | pre::-webkit-scrollbar {
72 | height: 8px;
73 | }
74 |
75 | /* Track */
76 | pre::-webkit-scrollbar-track {
77 | background: #000;
78 | }
79 |
80 | /* Handle */
81 | pre::-webkit-scrollbar-thumb {
82 | background: rgb(112,112,112);
83 | border-radius: 50px;
84 | }
85 | /* Handle on hover */
86 | pre::-webkit-scrollbar-thumb:hover {
87 | background: rgb(73, 73, 73);
88 |
89 | }
90 |
91 | .feature-image{
92 | width: 100%;
93 | height: 400px;
94 | }
95 |
96 | @media screen and (max-width: 600px){
97 |
98 | .images-inside-container{
99 | height: 200px;
100 | /* border:2px solid #000; */
101 | }
102 |
103 | .feature-image{
104 | height: 250px;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | My Blog in GatsbyJs 🔥🔥
11 |
12 |
13 | [](https://app.netlify.com/sites/hina-blogs-2002/deploys)
14 |
15 | ##### ⭐️ Live Demo : http://hina-blogs-2002.netlify.app/
16 |
17 | #### ⭐️ What tech I used:
18 |
19 | ⚡️ [Gatsby Js Minimal Starter](https://github.com/gatsbyjs/gatsby-starter-minimal)
20 | ⚡️ [Contentful (Headless CMS)](https://www.contentful.com/)
21 | ⚡️ [Material UI](https://material-ui.com)
22 | ⚡️ Firebase Authentication
23 | ⚡️ React Context API for state management
24 | ⚡️ Graphql
25 | ⚡️ Hosted on Netlify
26 |
27 |
28 |
29 | ###### ⭐️ Tech:
30 |
31 |
32 |

33 |

34 |

35 |

36 |

37 |

38 |
39 |
40 |
41 |
42 | ##### ⭐️ Preview :
43 |
44 | 
45 |
46 |
47 |
48 | 
49 |
50 |
51 |
52 |
53 | ## 🚀 Quick start
54 |
55 | 1. **Create a Gatsby site.**
56 |
57 | Use the Gatsby CLI to create a new site, specifying the minimal starter.
58 |
59 | ```shell
60 | # create a new Gatsby site using the minimal starter
61 | gatsby new my-site https://github.com/gatsbyjs/gatsby-starter-minimal
62 | ```
63 |
64 | 2. **Start developing.**
65 |
66 | Navigate into your new site’s directory and start it up.
67 |
68 | ```shell
69 | cd my-site/
70 | gatsby develop
71 | ```
72 |
73 | 3. **Open the code and start customizing!**
74 |
75 | Your site is now running at `http://localhost:8000`!
76 |
77 |
78 | > Any Feedback is appreciated.😃😃
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 |
3 | import Layout from '../components/Layout/Layout';
4 | import Blogs from '../components/BlogList/blogList';
5 |
6 | import { GlobalAuthProvider } from "../context/auth/auth";
7 | import { ModalContext, ModalContextProvider } from '../context/modal/modal';
8 |
9 | import * as classCss from '../style/modules/main.module.css';
10 | import {data } from '../../data';
11 | // material styles
12 | import { makeStyles } from '@material-ui/core/styles';
13 | import ModalSignIn from '../components/modal/modal';
14 | import Typography from '@material-ui/core/Typography';
15 |
16 |
17 | const useStyles = makeStyles((theme) => ({
18 | imageContainer: {
19 | width: "100%",
20 | height: "600px",
21 | overflow: "hidden",
22 | marginTop: '56px',
23 | },
24 | large: {
25 | width: theme.spacing(7),
26 | height: theme.spacing(7),
27 | },
28 | }));
29 |
30 |
31 |
32 | export default function Home() {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 |
43 | const Body = () => {
44 | const classes = useStyles();
45 | let { handleOpenLoginModal, blogsHeadingRef}= useContext(ModalContext);
46 |
47 | return (
48 | <>
49 |
50 |
51 |

52 |
55 |
58 | Hi Guys!
👋
59 | {data.name} here
60 | Future Software Engineer! 🥰🥰 |
61 | Love to Code❤️❤️
Full Stack Developer✨✨ | MERN Developer ☘️☘️|
62 | Python Developer⭐️⭐️ | Javascript Developer 🔥 🔥
63 |
64 |
65 |
66 |
67 |
68 | >
69 | )
70 | }
--------------------------------------------------------------------------------
/src/components/Blog/blogs.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { GlobalAuthProvider, AuthContext } from '../../context/auth/auth';
3 | import { ModalContextProvider} from '../../context/modal/modal';
4 |
5 | import './blogs.css';
6 |
7 | import { navigate } from "gatsby";
8 | import { MARKS } from '@contentful/rich-text-types';
9 | import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
10 |
11 | import Header from '../Header/header';
12 | import { Footer } from '../Footer/footer';
13 |
14 | import Button from '@material-ui/core/Button';
15 | import ArrowBackIcon from '@material-ui/icons/ArrowBack';
16 |
17 |
18 | function Blog(props) {
19 |
20 | const options = {
21 | renderMark: {
22 | [MARKS.CODE]: code => {code}
23 | },
24 | renderNode: {
25 | "embedded-asset-block": node => {
26 | const alt = node.data.target.fields.title["en-US"]
27 | const url = node.data.target.fields.file["en-US"].url
28 | return
29 | },
30 | },
31 | }
32 |
33 | let blog = props.pageContext;
34 | return (
35 |
36 |
37 |
38 | );
39 | }
40 |
41 | export default Blog;
42 |
43 |
44 |
45 |
46 |
47 | const BlogData = ({ blog, options, ...props }) => {
48 | let { state } = useContext(AuthContext);
49 |
50 | return (
51 |
52 |
53 |
54 |
61 |
62 | {
63 | state.isAuthenticated && !state.isLoading ?
64 | <>
65 |
{blog.title}
66 |
{blog.publishedDate}
67 |

68 |
{documentToReactComponents(blog.body, options)}
69 |
70 | >
71 | :
72 | (!state.isAuthenticated && !state.isLoading ?
Loading...
:
Error...
)
73 | }
74 |
75 |
76 |
77 | );
78 | }
--------------------------------------------------------------------------------
/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/context/auth/auth.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useEffect, useReducer, useState } from "react";
2 | import firebase from "firebase";
3 | import firebaseConfig from "../../firebase/firebaseConfig";
4 | import { authReducer } from "./authReducer";
5 | import Snackbar from "@material-ui/core/Snackbar";
6 | import MuiAlert from "@material-ui/lab/Alert";
7 |
8 | export const AuthContext = createContext(null);
9 |
10 | let initialState = {
11 | user: null,
12 | isAuthenticated: false,
13 | isLoading: false,
14 | error: null,
15 | };
16 |
17 | export const GlobalAuthProvider = (props) => {
18 | const [snackBar, setSnackBar] = useState(false);
19 | const [messageSnackBar, setMessageSnackBar] = useState("Log Out Failed!");
20 | let [state, dispatch] = useReducer(authReducer, initialState);
21 |
22 | const signUpwithGoogle = (callBack) => {
23 | var provider = new firebase.auth.GoogleAuthProvider();
24 |
25 | firebase
26 | .auth()
27 | .signInWithPopup(provider)
28 | .then(function (result) {
29 | var token = result.credential.accessToken;
30 | var user = result.user;
31 |
32 | dispatch({
33 | type: "LOGIN_SUCCESSFULLY",
34 | payload: user,
35 | accessToken: token,
36 | });
37 | callBack();
38 | })
39 | .catch(function (error) {
40 | dispatch({
41 | type: "LOGIN_FAILED",
42 | payload: error,
43 | });
44 | });
45 | };
46 |
47 | const onSignInWithFacebook = (callBack) => {
48 | var provider = new firebase.auth.FacebookAuthProvider();
49 |
50 | firebase
51 | .auth()
52 | .signInWithPopup(provider)
53 | .then(function (result) {
54 | var token = result.credential.accessToken;
55 | var user = result.user;
56 | dispatch({
57 | type: "LOGIN_SUCCESSFULLY",
58 | payload: user,
59 | accessToken: token,
60 | });
61 | callBack();
62 | })
63 | .catch(function (error) {
64 | dispatch({
65 | type: "LOGIN_FAILED",
66 | payload: error,
67 | });
68 | setMessageSnackBar("Login Failed!");
69 | setSnackBar(true);
70 | });
71 | };
72 |
73 | const getSignedInUser = async () => {
74 | await firebase.auth().onAuthStateChanged(function (user) {
75 | if (user) {
76 | dispatch({
77 | type: "LOADING",
78 | payload: user,
79 | });
80 | } else {
81 | console.log("sign out");
82 | dispatch({
83 | type: "LOADED",
84 | });
85 | }
86 | });
87 | };
88 |
89 | const signOut = () => {
90 | firebase
91 | .auth()
92 | .signOut()
93 | .then((res) => {
94 | console.log("Log out succesfully", res);
95 | dispatch({
96 | type: "SIGN_OUT",
97 | });
98 | })
99 | .catch((e) => {
100 | setMessageSnackBar("Logout Failed");
101 | setSnackBar(true);
102 | });
103 | };
104 |
105 | useEffect(() => {
106 | getSignedInUser();
107 | }, []);
108 |
109 | return (
110 |
113 | {props.children}
114 | setSnackBar(false)}
118 | anchorOrigin={{ vertical: "top", horizontal: "center" }}
119 | >
120 | setSnackBar(false)} severity="error">
121 | {messageSnackBar}
122 |
123 |
124 |
125 | );
126 | };
127 |
128 | function Alert(props) {
129 | return ;
130 | }
131 |
--------------------------------------------------------------------------------
/src/components/modal/modal.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext} from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Modal from '@material-ui/core/Modal';
4 | import Backdrop from '@material-ui/core/Backdrop';
5 | import Fade from '@material-ui/core/Fade';
6 | import Button from '@material-ui/core/Button';
7 | import { AuthContext } from '../../context/auth/auth';
8 | import "./modal.css";
9 |
10 |
11 | const useStyles = makeStyles((theme) => ({
12 | modal: {
13 | display: 'flex',
14 | alignItems: 'center',
15 | justifyContent: 'center',
16 | },
17 | paper: {
18 | textAlign: "center",
19 | width: "500px",
20 | borderRadius: "5px",
21 | margin: '0 15px',
22 | background: "#fff",
23 | boxShadow: theme.shadows[5],
24 | padding: theme.spacing(2, 4, 3),
25 | "&:focus":{
26 | outline:"none"
27 | }
28 | },
29 | button: {
30 | width: '-webkit-fill-available',
31 | margin: '20px 0',
32 | display: "flex",
33 | alignItems: 'center',
34 | border: "1px solid lightgrey",
35 | },
36 | }));
37 |
38 | export default function ModalSignIn({ open, handleClose}) {
39 | const classes = useStyles();
40 | const { signUpwithGoogle, state,onSignInWithFacebook } = useContext(AuthContext);
41 |
42 | const Signup = async () => {
43 | signUpwithGoogle(handleClose);
44 | }
45 | const SignInFaceBook = () => {
46 | onSignInWithFacebook(handleClose);
47 | }
48 |
49 | return (
50 |
62 |
63 |
64 |
Login to Hina Blogs
65 |
Login to read more articles.
66 |
80 | }
81 | >
82 | Login with Google
83 |
84 |
94 |
95 |
96 | );
97 | }
--------------------------------------------------------------------------------
/src/components/BlogList/blogList.tsx:
--------------------------------------------------------------------------------
1 | import React,{useContext,useState,useEffect} from 'react';
2 | import { navigate, useStaticQuery, graphql } from "gatsby";
3 | import "./blogList.css"
4 |
5 | import { AuthContext } from '../../context/auth/auth';
6 |
7 | // material styles
8 | import Pagination from '@material-ui/lab/Pagination';
9 | import Card from '@material-ui/core/Card';
10 | import CardActionArea from '@material-ui/core/CardActionArea';
11 | import CardActions from '@material-ui/core/CardActions';
12 | import CardContent from '@material-ui/core/CardContent';
13 | import CardMedia from '@material-ui/core/CardMedia';
14 | import Divider from '@material-ui/core/Divider';
15 | import Typography from '@material-ui/core/Typography';
16 | import Skeleton from '@material-ui/lab/Skeleton';
17 | import { makeStyles } from '@material-ui/core/styles';
18 |
19 |
20 | const useStyles = makeStyles((theme) => ({
21 | root: {
22 | display: "flex",
23 | margin: "20px 0",
24 | boxShadow: "none",
25 | alignItems: 'center',
26 | [theme.breakpoints.down('xs')]: {
27 | flexDirection: "column",
28 | textAlign:"center"
29 | },
30 | },
31 | media: {
32 | width: "300px",
33 | height: "200px",
34 | },
35 | imageButton: {
36 | width: '300px',
37 | height:"200px",
38 | margin: "0 30px",
39 | [theme.breakpoints.down('xs')]: {
40 | marginTop: '20px'
41 | },
42 | },
43 | readMore: {
44 | color: '#3f51b5',
45 | cursor: "pointer",
46 | fontWeight: 'bold',
47 | fontFamily: "'Karla', serif",
48 | [theme.breakpoints.down('xs')]: {
49 | width: '100%'
50 | },
51 | },
52 | cardBody: {
53 | [theme.breakpoints.down('xs')]: {
54 | margin: '20px 30px'
55 | },
56 | },
57 | date: {
58 | letterSpacing: '1.3px',
59 | fontFamily: "'Karla', sans-serif"
60 | },
61 | excerpt: {
62 | fontSize: "15px",
63 | fontFamily: "'Karla', sans-serif"
64 | },
65 | footer: {
66 | margin: '10px 0',
67 | padding: 0,
68 | },
69 | pagination: {
70 | padding: "30px 0",
71 | "& > nav > ul": {
72 | justifyContent: 'center'
73 | }
74 | }
75 | }));
76 |
77 | // karla font family
78 | // crimson text font family heading
79 |
80 | export default function Blogs({ handleOpenLoginModal,blogsHeadingRef }) {
81 | const classes = useStyles();
82 | const { state } = useContext(AuthContext);
83 | const [pageNumber, setPageNumber] = useState(1);
84 | const [imageLoader, setImageLoader] = useState(true);
85 |
86 |
87 | useEffect(() => {
88 | setTimeout(() => setImageLoader(false), 3000);
89 | },[])
90 |
91 |
92 | const onPageChange = (event, page) => {
93 | setPageNumber(page);
94 | }
95 |
96 |
97 | const onLink = (link) => {
98 | if (state.isAuthenticated && !state.isLoading) {
99 | navigate(link);
100 | }
101 | else {
102 | handleOpenLoginModal();
103 | }
104 | }
105 |
106 | const data = useStaticQuery(
107 | graphql`
108 | query {
109 | allContentfulBlogModel {
110 | nodes {
111 | title
112 | slug
113 | publishedDate(formatString: "MMMM DD, YYYY")
114 | excerpt {
115 | excerpt
116 | }
117 | featuredImage {
118 | fluid {
119 | src
120 | }
121 | }
122 | body {
123 | json
124 | }
125 |
126 | }
127 | }
128 | }
129 | `
130 | );
131 |
132 |
133 | // for pagination
134 | let totalPages = Math.ceil(data.allContentfulBlogModel.nodes.length / 5);
135 | let LastPostIndex = pageNumber * 5;
136 | let FirstPostIndex = LastPostIndex - 5;
137 | const currentBlogs = data.allContentfulBlogModel.nodes.slice(FirstPostIndex,LastPostIndex);
138 |
139 |
140 | return (
141 |
142 |
143 |
Latest Blogs
144 |
145 |
146 | {
147 | currentBlogs.map((blog, index) =>
148 |
149 |
150 | {
151 | imageLoader ?
152 | :
153 |
158 | }
159 |
160 |
161 |
162 | {blog.publishedDate}
163 |
164 | onLink("/blog/"+blog.slug)} gutterBottom variant="h5" component="h2">
165 | {blog.title}
166 |
167 |
168 | {blog.excerpt.excerpt}
169 |
170 |
171 | onLink("/blog/"+blog.slug)} color="textSecondary" component="p">
172 | Read More
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | )
183 | }
184 |
185 |
186 | {totalPages > 1 ?
187 |
:
188 |
}
189 |
190 |
191 |
192 | );
193 | }
194 |
195 |
--------------------------------------------------------------------------------
/src/components/Header/header.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState,useContext,useEffect, Ref} from "react";
2 | import { navigate } from 'gatsby';
3 |
4 | import "./header.css";
5 |
6 | import ModalSignIn from '../modal/modal';
7 |
8 | import { makeStyles } from '@material-ui/core/styles';
9 | import { useTheme } from '@material-ui/core/styles';
10 | import AppBar from '@material-ui/core/AppBar';
11 | import Toolbar from '@material-ui/core/Toolbar';
12 | import Typography from '@material-ui/core/Typography';
13 | import Button from '@material-ui/core/Button';
14 | import Menu from '@material-ui/core/Menu';
15 | import MenuItem from '@material-ui/core/MenuItem';
16 | import Avatar from '@material-ui/core/Avatar';
17 | import MenuIcon from '@material-ui/icons/Menu';
18 | import Drawer from '@material-ui/core/Drawer';
19 | import List from '@material-ui/core/List'
20 | import ListItem from '@material-ui/core/ListItem';
21 | import ListItemIcon from '@material-ui/core/ListItemIcon';
22 | import ListItemText from '@material-ui/core/ListItemText';
23 | import LibraryBooksIcon from '@material-ui/icons/LibraryBooks';
24 | import PersonIcon from '@material-ui/icons/Person';
25 | import ExitToAppIcon from '@material-ui/icons/ExitToApp';
26 | import ArrowBackIcon from '@material-ui/icons/ArrowBack';
27 |
28 | import { AuthContext } from '../../context/auth/auth';
29 | import { ModalContext, ModalContextProvider } from '../../context/modal/modal';
30 | import { data } from '../../../data';
31 |
32 |
33 |
34 | const useStyles = makeStyles((theme) => ({
35 | root: {
36 | flexGrow: 1,
37 | },
38 | menuButton: {
39 | marginRight: theme.spacing(2),
40 | },
41 | title: {
42 | flexGrow: 1,
43 | fontFamily: "'Karla', serif",
44 | },
45 | options: {
46 | fontFamily: "'Karla', serif",
47 | color: '#fff',
48 | fontSize: "1.2em",
49 | margin: '0 20px'
50 | },
51 | loginBtn: {
52 | margin: '0 15px',
53 | padding: '0',
54 | color: '#fff',
55 | fontSize: '1.2em',
56 | // textTransform:"Capitalize",
57 | fontFamily: "'Karla', serif",
58 | },
59 | image: {
60 | width: 'inherit',
61 | height:"inherit"
62 |
63 | },
64 | name: {
65 | margin: "0 10px"
66 | },
67 | menu: {
68 | cursor: "pointer",
69 | display: "flex",
70 | alignItems: 'center',
71 | textShadow:"0 2px 4px #000"
72 | },
73 | large: {
74 | width: theme.spacing(7),
75 | height: theme.spacing(7),
76 | [theme.breakpoints.down('xs')]: {
77 | margin: '20px auto',
78 | width: '80px',
79 | height: '80px'
80 | },
81 | },
82 | mobileProfile: {
83 | textAlign:"center"
84 | }
85 |
86 | }));
87 |
88 |
89 | export default function Header() {
90 |
91 | const classes = useStyles();
92 | const theme = useTheme();
93 | const [resize, setResize] = useState(undefined);
94 | const [anchorEl, setAnchorEl] = useState(false);
95 | const [drawerState, setDrawerState] = useState(false);
96 | const { state, signOut } = useContext(AuthContext);
97 | const { handleOpenLoginModal, blogsHeadingRef,openLoginModal, handleCloseLoginModal} = useContext(ModalContext);
98 |
99 |
100 | const toggleDrawer = (option) => {
101 | if (option === 'close') {
102 | setDrawerState(false);
103 | }
104 | else if (option == null) {
105 | setDrawerState(!drawerState);
106 | }
107 | }
108 |
109 | const onClickDropdownMenu = (event) => {
110 | setAnchorEl(event.currentTarget);
111 | };
112 |
113 | const onCloseDropdownMenu = () => {
114 | setAnchorEl(false);
115 | };
116 |
117 | const Signout = () => {
118 | signOut();
119 | if (window.location.pathname != '/') {
120 | navigate("/");
121 | }
122 | }
123 |
124 | const onScrollToBlogsPanel = (e) => {
125 | if (e.target.textContent === 'Blogs') {
126 | if (window.location.pathname === '/') {
127 | blogsHeadingRef.current.scrollIntoView({
128 | behavior: "smooth", block: "start"
129 | })
130 | }
131 | else {
132 | navigate("/");
133 | }
134 | }
135 | }
136 |
137 | useEffect(() => {
138 | setResize(window.innerWidth);
139 | window.addEventListener('resize', () => setResize(window.innerWidth));
140 | return () => window.removeEventListener("resize", () => setResize(window.innerWidth));
141 | }, []);
142 |
143 |
144 | return (
145 |
146 |
147 |
148 |
149 |
150 | {
151 | data.name
152 | }
153 |
154 |
155 | {
156 | theme.breakpoints.values.sm <= resize ? <>
157 |
158 |
161 | {
162 | state.isAuthenticated && !state.isLoading ?
163 | <>
164 |
165 |
166 |
{state.user?.displayName}
167 |
168 |
178 |
179 | > :
180 |
181 |
182 | }
183 | > :
184 | <>
185 | toggleDrawer(null)} />
186 | toggleDrawer('close')}>
187 |
188 |
189 |
190 | {
191 | state.isAuthenticated && !state.isLoading ?
192 |
193 |
194 |
{state.user?.displayName}
195 |
{state.user?.email}
196 |
:
197 | null
198 | }
199 |
200 | {toggleDrawer("close"); onScrollToBlogsPanel(e); }} button key={"blogs"}>
201 |
202 |
203 |
204 |
205 | {
206 | navigate("/about");
207 | toggleDrawer("close");
208 | }} button key={"About"}>
209 |
210 |
211 |
212 |
213 | {
214 | state.isAuthenticated && !state.isLoading ?
215 |
216 |
217 |
218 |
219 | :
220 | { handleOpenLoginModal(); setDrawerState(false); }} button key={"Login"}>
221 |
222 |
223 |
224 | }
225 |
226 |
227 |
228 |
229 | >
230 | }
231 |
232 |
233 |
234 | );
235 | }
236 |
--------------------------------------------------------------------------------
/assets/netlify.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------