Add dynamic meta tags

This commit is contained in:
MD. Ariful Alam 2021-08-25 22:53:07 +06:00
parent 8eb2b4b09f
commit 13111a3106
10 changed files with 178 additions and 60 deletions

65
package-lock.json generated
View File

@ -19,6 +19,7 @@
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet-async": "^1.1.0",
"react-icons": "^4.2.0",
"react-redux": "^7.2.4",
"react-scripts": "4.0.3",
@ -10091,6 +10092,14 @@
"node": ">= 0.4"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@ -16232,6 +16241,27 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
},
"node_modules/react-fast-compare": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"node_modules/react-helmet-async": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.1.0.tgz",
"integrity": "sha512-550xWSW61j7Qzk26GBOdh096wKG6n1jY3ew7bH5NU0GvIZ5LyeuBLxHFBddMP7ZpsSltnUGzzh2p6Rj+aQAi4g==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"invariant": "^2.2.4",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.2.0",
"shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": "^16.6.0 || ^17.0.0",
"react-dom": "^16.6.0 || ^17.0.0"
}
},
"node_modules/react-icons": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
@ -17760,6 +17790,11 @@
"sha.js": "bin.js"
}
},
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -29487,6 +29522,14 @@
"side-channel": "^1.0.4"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@ -34355,6 +34398,23 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
},
"react-fast-compare": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"react-helmet-async": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.1.0.tgz",
"integrity": "sha512-550xWSW61j7Qzk26GBOdh096wKG6n1jY3ew7bH5NU0GvIZ5LyeuBLxHFBddMP7ZpsSltnUGzzh2p6Rj+aQAi4g==",
"requires": {
"@babel/runtime": "^7.12.5",
"invariant": "^2.2.4",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.2.0",
"shallowequal": "^1.1.0"
}
},
"react-icons": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
@ -35566,6 +35626,11 @@
"safe-buffer": "^5.0.1"
}
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",

View File

@ -15,6 +15,7 @@
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet-async": "^1.1.0",
"react-icons": "^4.2.0",
"react-redux": "^7.2.4",
"react-scripts": "4.0.3",

View File

@ -1,52 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
</script>
<!-- End Google Analytics -->
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Portfolio</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -15,11 +15,7 @@ import Education from "./components/Education";
import Project from "./components/Project";
import { setRepo } from "./store/slices/repoSlice";
import Blog from "./components/Blog";
import { ga } from "./helpers/utils";
if (config.googleAnalytics.id) {
ga.initialize(config.googleAnalytics.id);
}
import MetaTags from "./components/MetaTags";
function App() {
const dispatch = useDispatch();
@ -116,6 +112,7 @@ function App() {
return (
<Fragment>
<MetaTags/>
<div className="fade-in h-screen">
{

View File

@ -86,6 +86,7 @@ const Blog = () => {
className="card shadow-lg compact bg-base-100 cursor-pointer"
key={index}
onClick={() => {
try {
if (config.googleAnalytics.id) {
ga.event({
action: "Click Blog Post",
@ -94,6 +95,9 @@ const Blog = () => {
}
});
}
} catch (error) {
console.error(error);
}
window.open(article.link, '_blank')
}}

View File

@ -0,0 +1,62 @@
import React, { Fragment } from 'react';
import { Helmet } from "react-helmet-async";
import { useSelector } from 'react-redux';
import config from '../config';
import { isThemeDarkish } from '../helpers/utils';
const MetaTags = () => {
const profile = useSelector(state => state.profile);
const theme = useSelector(state => state.theme);
return (
<Fragment>
{
profile && (
<Helmet>
{
config.googleAnalytics.id && (
<script async src={`https://www.googletagmanager.com/gtag/js?id=${config.googleAnalytics.id}`}></script>
)
}
{
config.googleAnalytics.id && (
<script>
{
`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${config.googleAnalytics.id}');
`
}
</script>
)
}
<title>Portfolio of {profile.name}</title>
<meta name="theme-color" content={isThemeDarkish(theme) ? '#000000' : '#ffffff'}/>
<meta name="description" content={profile.bio} />
<meta itemprop="name" content={`Portfolio of ${profile.name}`} />
<meta itemprop="description" content={profile.bio} />
<meta itemprop="image" content={profile.avatar} />
<meta property="og:url" content={typeof config.social.website !== 'undefined' ? config.social.website : ''} />
<meta property="og:type" content="website" />
<meta property="og:title" content={`Portfolio of ${profile.name}`} />
<meta property="og:description" content={profile.bio} />
<meta property="og:image" content={profile.avatar} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={`Portfolio of ${profile.name}`} />
<meta name="twitter:description" content={profile.bio} />
<meta name="twitter:image" content={profile.avatar} />
</Helmet>
)
}
</Fragment>
)
}
export default MetaTags;

View File

@ -56,6 +56,7 @@ const Project = () => {
className="card shadow-lg compact bg-base-100 cursor-pointer"
key={index}
onClick={() => {
try {
if (config.googleAnalytics.id) {
ga.event({
action: "Click project",
@ -64,6 +65,9 @@ const Project = () => {
}
});
}
} catch (error) {
console.error(error);
}
window.open(item.html_url, '_blank')
}}

View File

@ -53,3 +53,18 @@ export const ga = {
}
}
}
export const isThemeDarkish = (theme) => {
if (
theme === 'dark' ||
theme === 'halloween' ||
theme === 'forest' ||
theme === 'black' ||
theme === 'luxury' ||
theme === 'dracula'
) {
return true;
} else {
return false;
}
}

View File

@ -5,11 +5,14 @@ import App from './App';
import { Provider } from 'react-redux';
import reportWebVitals from './reportWebVitals';
import { store } from './store/store';
import { HelmetProvider } from 'react-helmet-async';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<HelmetProvider>
<App/>
</HelmetProvider>
</Provider>
</React.StrictMode>,
document.getElementById('root')

View File

@ -26,6 +26,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
--z-primary: red;
}
code {