fixed helmet issues and refactored SEO
This commit is contained in:
parent
6fd4a94a6a
commit
0465e97cf0
15 changed files with 201 additions and 65 deletions
|
@ -26,6 +26,7 @@ module.exports = {
|
||||||
title: 'Spee.ch',
|
title: 'Spee.ch',
|
||||||
name : 'Spee.ch',
|
name : 'Spee.ch',
|
||||||
host : 'https://spee.ch',
|
host : 'https://spee.ch',
|
||||||
|
description: 'Open-source, decentralized image and video sharing.'
|
||||||
},
|
},
|
||||||
claim: {
|
claim: {
|
||||||
defaultTitle : 'Spee.ch',
|
defaultTitle : 'Spee.ch',
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { StaticRouter } from 'react-router-dom';
|
||||||
import GAListener from '../react/components/GAListener';
|
import GAListener from '../react/components/GAListener';
|
||||||
import App from '../react/app';
|
import App from '../react/app';
|
||||||
import renderFullPage from './renderFullPage.js';
|
import renderFullPage from './renderFullPage.js';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
module.exports = (req, res) => {
|
||||||
let context = {};
|
let context = {};
|
||||||
|
@ -25,6 +26,9 @@ module.exports = (req, res) => {
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// get head tags from helmet
|
||||||
|
const helmet = Helmet.renderStatic();
|
||||||
|
|
||||||
// check for a redirect
|
// check for a redirect
|
||||||
if (context.url) {
|
if (context.url) {
|
||||||
// Somewhere a `<Redirect>` was rendered
|
// Somewhere a `<Redirect>` was rendered
|
||||||
|
@ -37,5 +41,5 @@ module.exports = (req, res) => {
|
||||||
const preloadedState = store.getState();
|
const preloadedState = store.getState();
|
||||||
|
|
||||||
// send the rendered page back to the client
|
// send the rendered page back to the client
|
||||||
res.send(renderFullPage(html, preloadedState));
|
res.send(renderFullPage(helmet, html, preloadedState));
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,6 @@ export function getChannelData (name, id) {
|
||||||
export function getChannelClaims (name, longId, page) {
|
export function getChannelClaims (name, longId, page) {
|
||||||
console.log('getting channel claims for channel:', name, longId);
|
console.log('getting channel claims for channel:', name, longId);
|
||||||
if (!page) page = 1;
|
if (!page) page = 1;
|
||||||
const url = `/api/channel/claims/${name}/${longId}/${page}`;
|
const url = `${host}/api/channel/claims/${name}/${longId}/${page}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createBasicCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createBasicMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class AboutPage extends React.Component {
|
class AboutPage extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
|
const pageTitle = createPageTitle('About');
|
||||||
|
const canonicalLink = createBasicCanonicalLink('about');
|
||||||
|
const metaTags = createBasicMetaTags();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title} - About</title>
|
|
||||||
<link rel='canonical' href={`${host}/about`} />
|
|
||||||
</Helmet>
|
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className='row row--padded'>
|
<div className='row row--padded'>
|
||||||
<div className='column column--5 column--med-10 align-content-top'>
|
<div className='column column--5 column--med-10 align-content-top'>
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import PublishTool from 'containers/PublishTool';
|
import PublishTool from 'containers/PublishTool';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createBasicCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createBasicMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class HomePage extends React.Component {
|
class HomePage extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
|
const pageTitle = createPageTitle();
|
||||||
|
const canonicalLink = createBasicCanonicalLink();
|
||||||
|
const metaTags = createBasicMetaTags();
|
||||||
return (
|
return (
|
||||||
<div className={'row row--tall flex-container--column'}>
|
<div className={'row row--tall flex-container--column'}>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title}</title>
|
|
||||||
<link rel='canonical' href={`${host}/`} />
|
|
||||||
</Helmet>
|
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className={'row row--tall row--padded flex-container--column'}>
|
<div className={'row row--tall row--padded flex-container--column'}>
|
||||||
<PublishTool />
|
<PublishTool />
|
||||||
|
|
|
@ -74,7 +74,7 @@ class OpenGraphTags extends React.Component {
|
||||||
{/* image twitter tags */}
|
{/* image twitter tags */}
|
||||||
<meta name='twitter:card' content='summary_large_image' />
|
<meta name='twitter:card' content='summary_large_image' />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
)};
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
24
react/components/SEO/index.jsx
Normal file
24
react/components/SEO/index.jsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
class SEO extends React.Component {
|
||||||
|
render () {
|
||||||
|
const { pageTitle, metaTags, canonicalLink } = this.props;
|
||||||
|
return (
|
||||||
|
<Helmet
|
||||||
|
title={pageTitle}
|
||||||
|
link={[{rel: 'canonical', href: canonicalLink}]}
|
||||||
|
meta={metaTags}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SEO.propTypes = {
|
||||||
|
pageTitle : PropTypes.string.isRequired,
|
||||||
|
metaTags : PropTypes.array.isRequired,
|
||||||
|
canonicalLink: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SEO;
|
|
@ -1,33 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
import OpenGraphTags from 'components/OpenGraphTags';
|
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import ErrorPage from 'components/ErrorPage';
|
import ErrorPage from 'components/ErrorPage';
|
||||||
import AssetTitle from 'components/AssetTitle';
|
import AssetTitle from 'components/AssetTitle';
|
||||||
import AssetDisplay from 'components/AssetDisplay';
|
import AssetDisplay from 'components/AssetDisplay';
|
||||||
import AssetInfo from 'components/AssetInfo';
|
import AssetInfo from 'components/AssetInfo';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createAssetCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createAssetMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class ShowAssetDetails extends React.Component {
|
class ShowAssetDetails extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
const { asset } = this.props;
|
const { asset } = this.props;
|
||||||
if (asset) {
|
if (asset) {
|
||||||
let channelName, certificateId, name, claimId;
|
const { name } = asset.claimData;
|
||||||
if (asset.claimData) {
|
const pageTitle = createPageTitle(`${name} - details`);
|
||||||
({ channelName, certificateId, name, claimId } = asset.claimData);
|
const canonicalLink = createAssetCanonicalLink(asset);
|
||||||
};
|
const metaTags = createAssetMetaTags(asset);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title} - {name} - details</title>
|
|
||||||
{channelName ? (
|
|
||||||
<link rel='canonical' href={`${host}/${channelName}:${certificateId}/${name}`} />
|
|
||||||
) : (
|
|
||||||
<link rel='canonical' href={`${host}/${claimId}/${name}`} />
|
|
||||||
)}
|
|
||||||
</Helmet>
|
|
||||||
<OpenGraphTags />
|
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className='row row--tall row--padded'>
|
<div className='row row--tall row--padded'>
|
||||||
<div className='column column--10'>
|
<div className='column column--10'>
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
import OpenGraphTags from 'components/OpenGraphTags';
|
import OpenGraphTags from 'components/OpenGraphTags';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import AssetDisplay from 'components/AssetDisplay';
|
import AssetDisplay from 'components/AssetDisplay';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createAssetCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createAssetMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class ShowLite extends React.Component {
|
class ShowLite extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
const { asset } = this.props;
|
const { asset } = this.props;
|
||||||
if (asset) {
|
if (asset) {
|
||||||
let channelName, certificateId, name, claimId, fileExt;
|
const { name, claimId } = asset.claimData;
|
||||||
if (asset.claimData) {
|
const pageTitle = createPageTitle(name);
|
||||||
({ channelName, certificateId, name, claimId, fileExt } = asset.claimData);
|
const canonicalLink = createAssetCanonicalLink(asset);
|
||||||
};
|
const metaTags = createAssetMetaTags(asset);
|
||||||
return (
|
return (
|
||||||
<div className='row row--tall flex-container--column flex-container--center-center'>
|
<div className='row row--tall flex-container--column flex-container--center-center'>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title} - {name}</title>
|
<OpenGraphTags />
|
||||||
{channelName ? (
|
|
||||||
<link rel='canonical' href={`${host}/${channelName}:${certificateId}/${name}.${fileExt}`} />
|
|
||||||
) : (
|
|
||||||
<link rel='canonical' href={`${host}/${claimId}/${name}.${fileExt}`} />
|
|
||||||
)}
|
|
||||||
<OpenGraphTags />
|
|
||||||
</Helmet>
|
|
||||||
<div>
|
<div>
|
||||||
<AssetDisplay />
|
<AssetDisplay />
|
||||||
<Link id='asset-boilerpate' className='link--primary fine-print' to={`/${claimId}/${name}`}>hosted
|
<Link id='asset-boilerpate' className='link--primary fine-print' to={`/${claimId}/${name}`}>hosted
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
import ErrorPage from 'components/ErrorPage';
|
import ErrorPage from 'components/ErrorPage';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import ChannelClaimsDisplay from 'containers/ChannelClaimsDisplay';
|
import ChannelClaimsDisplay from 'containers/ChannelClaimsDisplay';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createChannelCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createChannelMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class ShowChannel extends React.Component {
|
class ShowChannel extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
const { channel } = this.props;
|
const { channel } = this.props;
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const { name, longId, shortId } = channel;
|
const { name, longId, shortId } = channel;
|
||||||
|
const pageTitle = createPageTitle(`${name}`);
|
||||||
|
const canonicalLink = createChannelCanonicalLink(channel);
|
||||||
|
const metaTags = createChannelMetaTags(channel);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title} - {name}</title>
|
|
||||||
<link rel='canonical' href={`${host}/${name}:${longId}`} />
|
|
||||||
</Helmet>
|
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className='row row--tall row--padded'>
|
<div className='row row--tall row--padded'>
|
||||||
<div className='column column--10'>
|
<div className='column column--10'>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import Helmet from 'react-helmet';
|
import SEO from 'components/SEO';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import ChannelLoginForm from 'containers/ChannelLoginForm';
|
import ChannelLoginForm from 'containers/ChannelLoginForm';
|
||||||
import ChannelCreateForm from 'containers/ChannelCreateForm';
|
import ChannelCreateForm from 'containers/ChannelCreateForm';
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
import { createBasicCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
import { createBasicMetaTags } from 'utils/metaTags';
|
||||||
|
|
||||||
class LoginPage extends React.Component {
|
class LoginPage extends React.Component {
|
||||||
componentWillReceiveProps (newProps) {
|
componentWillReceiveProps (newProps) {
|
||||||
|
@ -16,12 +17,12 @@ class LoginPage extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
const pageTitle = createPageTitle('Login');
|
||||||
|
const canonicalLink = createBasicCanonicalLink('login');
|
||||||
|
const metaTags = createBasicMetaTags();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<SEO pageTitle={pageTitle} canonicalLink={canonicalLink} metaTags={metaTags} />
|
||||||
<title>{title} - Login</title>
|
|
||||||
<link rel='canonical' href={`${host}/login`} />
|
|
||||||
</Helmet>
|
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className='row row--padded'>
|
<div className='row row--padded'>
|
||||||
<div className='column column--5 column--med-10 align-content-top'>
|
<div className='column column--5 column--med-10 align-content-top'>
|
||||||
|
|
|
@ -50,7 +50,6 @@ function * parseAndUpdateClaimOnly (claim) {
|
||||||
|
|
||||||
export function * handleShowPageUri (action) {
|
export function * handleShowPageUri (action) {
|
||||||
console.log('handleShowPageUri');
|
console.log('handleShowPageUri');
|
||||||
console.log('action:', action);
|
|
||||||
const { identifier, claim } = action.data;
|
const { identifier, claim } = action.data;
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
return yield call(parseAndUpdateIdentifierAndClaim, identifier, claim);
|
return yield call(parseAndUpdateIdentifierAndClaim, identifier, claim);
|
||||||
|
|
24
react/utils/canonicalLink.js
Normal file
24
react/utils/canonicalLink.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const { site: { host } } = require('../../config/speechConfig.js');
|
||||||
|
|
||||||
|
export const createBasicCanonicalLink = (page) => {
|
||||||
|
if (!page) {
|
||||||
|
return `${host}`;
|
||||||
|
};
|
||||||
|
return `${host}/${page}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createAssetCanonicalLink = (asset) => {
|
||||||
|
let channelName, certificateId, name, claimId;
|
||||||
|
if (asset.claimData) {
|
||||||
|
({ channelName, certificateId, name, claimId } = asset.claimData);
|
||||||
|
};
|
||||||
|
if (channelName) {
|
||||||
|
return `${host}/${channelName}:${certificateId}/${name}`;
|
||||||
|
};
|
||||||
|
return `${host}/${claimId}/${name}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createChannelCanonicalLink = (channel) => {
|
||||||
|
const { name, longId } = channel;
|
||||||
|
return `${host}/${name}:${longId}`;
|
||||||
|
};
|
86
react/utils/metaTags.js
Normal file
86
react/utils/metaTags.js
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
const { site: { title, host, description }, claim: { defaultThumbnail, defaultDescription } } = require('../../config/speechConfig.js');
|
||||||
|
|
||||||
|
const determineOgThumbnailContentType = (thumbnail) => {
|
||||||
|
if (thumbnail) {
|
||||||
|
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
|
||||||
|
switch (fileExt) {
|
||||||
|
case 'jpeg':
|
||||||
|
case 'jpg':
|
||||||
|
return 'image/jpeg';
|
||||||
|
case 'png':
|
||||||
|
return 'image/png';
|
||||||
|
case 'gif':
|
||||||
|
return 'image/gif';
|
||||||
|
case 'mp4':
|
||||||
|
return 'video/mp4';
|
||||||
|
default:
|
||||||
|
return 'image/jpeg';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBasicMetaTags = () => {
|
||||||
|
return [
|
||||||
|
{property: 'og:title', content: title},
|
||||||
|
{property: 'og:url', content: host},
|
||||||
|
{property: 'og:site_name', content: title},
|
||||||
|
{property: 'og:description', content: description},
|
||||||
|
{property: 'twitter:site', content: '@spee_ch'},
|
||||||
|
{property: 'twitter:card', content: 'summary'},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createChannelMetaTags = (channel) => {
|
||||||
|
const { name, longId } = channel;
|
||||||
|
return [
|
||||||
|
{property: 'og:title', content: `${name} on ${title}`},
|
||||||
|
{property: 'og:url', content: `${host}/${name}:${longId}`},
|
||||||
|
{property: 'og:site_name', content: title},
|
||||||
|
{property: 'og:description', content: `${name}, a channel on ${title}`},
|
||||||
|
{property: 'twitter:site', content: '@spee_ch'},
|
||||||
|
{property: 'twitter:card', content: 'summary'},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createAssetMetaTags = (asset) => {
|
||||||
|
const { claimData } = asset;
|
||||||
|
const { contentType } = claimData;
|
||||||
|
const embedUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
||||||
|
const showUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
||||||
|
const source = `${host}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
|
||||||
|
const ogTitle = claimData.title || claimData.name;
|
||||||
|
const ogDescription = claimData.description || defaultDescription;
|
||||||
|
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
|
||||||
|
const ogThumbnail = claimData.thumbnail || defaultThumbnail;
|
||||||
|
const metaTags = [
|
||||||
|
{property: 'og:title', content: ogTitle},
|
||||||
|
{property: 'og:url', content: showUrl},
|
||||||
|
{property: 'og:site_name', content: title},
|
||||||
|
{property: 'og:description', content: ogDescription},
|
||||||
|
{property: 'og:image:width', content: 600},
|
||||||
|
{property: 'og:image:height', content: 315},
|
||||||
|
{property: 'twitter:site', content: '@spee_ch'},
|
||||||
|
];
|
||||||
|
if (contentType === 'video/mp4' || contentType === 'video/webm') {
|
||||||
|
metaTags.push({property: 'og:video', content: source});
|
||||||
|
metaTags.push({property: 'og:video:secure_url', content: source});
|
||||||
|
metaTags.push({property: 'og:video:type', content: contentType});
|
||||||
|
metaTags.push({property: 'og:image', content: ogThumbnail});
|
||||||
|
metaTags.push({property: 'og:image:type', content: ogThumbnailContentType});
|
||||||
|
metaTags.push({property: 'og:type', content: 'video'});
|
||||||
|
metaTags.push({property: 'twitter:card', content: 'player'});
|
||||||
|
metaTags.push({property: 'twitter:player', content: embedUrl});
|
||||||
|
metaTags.push({property: 'twitter:player:width', content: 600});
|
||||||
|
metaTags.push({property: 'twitter:text:player_width', content: 600});
|
||||||
|
metaTags.push({property: 'twitter:player:height', content: 337});
|
||||||
|
metaTags.push({property: 'twitter:player:stream', content: source});
|
||||||
|
metaTags.push({property: 'twitter:player:stream:content_type', content: contentType});
|
||||||
|
} else {
|
||||||
|
metaTags.push({property: 'og:image', content: source});
|
||||||
|
metaTags.push({property: 'og:image:type', content: contentType});
|
||||||
|
metaTags.push({property: 'og:type', content: 'article'});
|
||||||
|
metaTags.push({property: 'twitter:card', content: 'summary_large_image'});
|
||||||
|
}
|
||||||
|
return metaTags;
|
||||||
|
};
|
8
react/utils/pageTitle.js
Normal file
8
react/utils/pageTitle.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const { site: { title: siteTitle } } = require('../../config/speechConfig.js');
|
||||||
|
|
||||||
|
export const createPageTitle = (pageTitle) => {
|
||||||
|
if (!pageTitle) {
|
||||||
|
return `${siteTitle}`;
|
||||||
|
}
|
||||||
|
return `${siteTitle} - ${pageTitle}`;
|
||||||
|
};
|
Loading…
Reference in a new issue