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 -->
<head>
<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.
<title>Portfolio</title>
</head>
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>
</head>
<body>
<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.
</body>
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 {