Add dynamic meta tags
This commit is contained in:
parent
8eb2b4b09f
commit
13111a3106
65
package-lock.json
generated
65
package-lock.json
generated
@ -19,6 +19,7 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-helmet-async": "^1.1.0",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.4",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
@ -10091,6 +10092,14 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
"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": {
|
"node_modules/react-icons": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
|
||||||
@ -17760,6 +17790,11 @@
|
|||||||
"sha.js": "bin.js"
|
"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": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -29487,6 +29522,14 @@
|
|||||||
"side-channel": "^1.0.4"
|
"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": {
|
"ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
"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": {
|
"react-icons": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz",
|
||||||
@ -35566,6 +35626,11 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"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": {
|
"shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-helmet-async": "^1.1.0",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.4",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
|
|||||||
@ -1,52 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<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" />
|
<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" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<!--
|
<title>Portfolio</title>
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
</head>
|
||||||
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
|
<body>
|
||||||
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>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
</body>
|
||||||
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.
|
</html>
|
||||||
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>
|
|
||||||
@ -15,11 +15,7 @@ import Education from "./components/Education";
|
|||||||
import Project from "./components/Project";
|
import Project from "./components/Project";
|
||||||
import { setRepo } from "./store/slices/repoSlice";
|
import { setRepo } from "./store/slices/repoSlice";
|
||||||
import Blog from "./components/Blog";
|
import Blog from "./components/Blog";
|
||||||
import { ga } from "./helpers/utils";
|
import MetaTags from "./components/MetaTags";
|
||||||
|
|
||||||
if (config.googleAnalytics.id) {
|
|
||||||
ga.initialize(config.googleAnalytics.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -116,6 +112,7 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
<MetaTags/>
|
||||||
<div className="fade-in h-screen">
|
<div className="fade-in h-screen">
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -86,13 +86,17 @@ const Blog = () => {
|
|||||||
className="card shadow-lg compact bg-base-100 cursor-pointer"
|
className="card shadow-lg compact bg-base-100 cursor-pointer"
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (config.googleAnalytics.id) {
|
try {
|
||||||
ga.event({
|
if (config.googleAnalytics.id) {
|
||||||
action: "Click Blog Post",
|
ga.event({
|
||||||
params: {
|
action: "Click Blog Post",
|
||||||
post: article.title
|
params: {
|
||||||
}
|
post: article.title
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.open(article.link, '_blank')
|
window.open(article.link, '_blank')
|
||||||
|
|||||||
62
src/components/MetaTags.js
Normal file
62
src/components/MetaTags.js
Normal 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;
|
||||||
@ -56,13 +56,17 @@ const Project = () => {
|
|||||||
className="card shadow-lg compact bg-base-100 cursor-pointer"
|
className="card shadow-lg compact bg-base-100 cursor-pointer"
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (config.googleAnalytics.id) {
|
try {
|
||||||
ga.event({
|
if (config.googleAnalytics.id) {
|
||||||
action: "Click project",
|
ga.event({
|
||||||
params: {
|
action: "Click project",
|
||||||
project: item.name
|
params: {
|
||||||
}
|
project: item.name
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.open(item.html_url, '_blank')
|
window.open(item.html_url, '_blank')
|
||||||
|
|||||||
@ -52,4 +52,19 @@ export const ga = {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isThemeDarkish = (theme) => {
|
||||||
|
if (
|
||||||
|
theme === 'dark' ||
|
||||||
|
theme === 'halloween' ||
|
||||||
|
theme === 'forest' ||
|
||||||
|
theme === 'black' ||
|
||||||
|
theme === 'luxury' ||
|
||||||
|
theme === 'dracula'
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,11 +5,14 @@ import App from './App';
|
|||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import { store } from './store/store';
|
import { store } from './store/store';
|
||||||
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App/>
|
<HelmetProvider>
|
||||||
|
<App/>
|
||||||
|
</HelmetProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
|
|||||||
@ -26,6 +26,7 @@ body {
|
|||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
--z-primary: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user