Folder structure #398
9 changed files with 114 additions and 92 deletions
|
@ -3,18 +3,19 @@ function SiteConfig () {
|
||||||
googleId: 'default',
|
googleId: 'default',
|
||||||
};
|
};
|
||||||
this.assetDefaults = {
|
this.assetDefaults = {
|
||||||
title : 'Spee.ch',
|
description: 'An asset published on Spee.ch',
|
||||||
thumbnail : 'https://spee.ch/assets/img/video_thumb_default.png',
|
thumbnail : 'https://spee.ch/assets/img/video_thumb_default.png',
|
||||||
description: 'Open-source, decentralized image and video sharing.',
|
title : 'Spee.ch',
|
||||||
};
|
};
|
||||||
this.auth = {
|
this.auth = {
|
||||||
sessionKey: 'default',
|
sessionKey: 'default',
|
||||||
};
|
};
|
||||||
this.details = {
|
this.details = {
|
||||||
|
description: 'Open-source, decentralized image and video sharing.',
|
||||||
|
host : 'default',
|
||||||
port : 3000,
|
port : 3000,
|
||||||
title : 'Spee.ch',
|
title : 'Spee.ch',
|
||||||
host : 'default',
|
twitter : '@spee_ch',
|
||||||
description: 'Open-source, decentralized image and video sharing.',
|
|
||||||
};
|
};
|
||||||
this.publishing = {
|
this.publishing = {
|
||||||
additionalClaimAddresses: [],
|
additionalClaimAddresses: [],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
class Preview extends React.Component {
|
class PublishPreview extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -53,10 +53,10 @@ class Preview extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.propTypes = {
|
PublishPreview.propTypes = {
|
||||||
dimPreview: PropTypes.bool.isRequired,
|
dimPreview: PropTypes.bool.isRequired,
|
||||||
file : PropTypes.object.isRequired,
|
file : PropTypes.object.isRequired,
|
||||||
thumbnail : PropTypes.object,
|
thumbnail : PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Preview;
|
export default PublishPreview;
|
|
@ -1,32 +1,16 @@
|
||||||
import React from 'react';
|
import { connect } from 'react-redux';
|
||||||
import Helmet from 'react-helmet';
|
import View from './view';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { createPageTitle } from 'utils/pageTitle';
|
const mapStateToProps = ({ site }) => {
|
||||||
import { createMetaTags } from 'utils/metaTags';
|
const { defaultDescription, defaultThumbnail, description: siteDescription, host: siteHost, title: siteTitle, twitter: siteTwitter } = site;
|
||||||
import { createCanonicalLink } from 'utils/canonicalLink';
|
return {
|
||||||
|
defaultDescription,
|
||||||
class SEO extends React.Component {
|
defaultThumbnail,
|
||||||
render () {
|
siteDescription,
|
||||||
let { pageTitle, asset, channel, pageUri } = this.props;
|
siteHost,
|
||||||
pageTitle = createPageTitle(pageTitle);
|
siteTitle,
|
||||||
const metaTags = createMetaTags(asset, channel);
|
siteTwitter,
|
||||||
const canonicalLink = createCanonicalLink(asset, channel, pageUri);
|
};
|
||||||
return (
|
|
||||||
<Helmet
|
|
||||||
title={pageTitle}
|
|
||||||
meta={metaTags}
|
|
||||||
link={[{rel: 'canonical', href: canonicalLink}]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SEO.propTypes = {
|
export default connect(mapStateToProps, null)(View);
|
||||||
pageTitle: PropTypes.string,
|
|
||||||
pageUri : PropTypes.string,
|
|
||||||
channel : PropTypes.object,
|
|
||||||
asset : PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SEO;
|
|
||||||
|
|
38
react/components/SEO/view.jsx
Normal file
38
react/components/SEO/view.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { createPageTitle } from 'utils/pageTitle';
|
||||||
|
import { createMetaTags } from 'utils/metaTags';
|
||||||
|
import { createCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
|
||||||
|
class SEO extends React.Component {
|
||||||
|
render () {
|
||||||
|
// props from state
|
||||||
|
const { defaultDescription, defaultThumbnail, siteDescription, siteHost, siteTitle, siteTwitter } = this.props;
|
||||||
|
// props from parent
|
||||||
|
const { asset, channel, pageUri } = this.props;
|
||||||
|
let { pageTitle } = this.props;
|
||||||
|
// create page title, tags, and canonical link
|
||||||
|
pageTitle = createPageTitle(siteTitle, pageTitle);
|
||||||
|
const metaTags = createMetaTags(siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail);
|
||||||
|
const canonicalLink = createCanonicalLink(asset, channel, pageUri, siteHost);
|
||||||
|
// render results
|
||||||
|
return (
|
||||||
|
<Helmet
|
||||||
|
title={pageTitle}
|
||||||
|
meta={metaTags}
|
||||||
|
link={[{rel: 'canonical', href: canonicalLink}]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SEO.propTypes = {
|
||||||
|
pageTitle: PropTypes.string,
|
||||||
|
pageUri : PropTypes.string,
|
||||||
|
channel : PropTypes.object,
|
||||||
|
asset : PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SEO;
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { validateFile } from 'utils/file';
|
import { validateFile } from 'utils/file';
|
||||||
import Preview from 'components/Preview';
|
import PublishPreview from 'components/PublishPreview';
|
||||||
|
|
||||||
class Dropzone extends React.Component {
|
class Dropzone extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
|
@ -87,7 +87,7 @@ class Dropzone extends React.Component {
|
||||||
<div id='preview-dropzone' className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
<div id='preview-dropzone' className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
||||||
{this.props.file ? (
|
{this.props.file ? (
|
||||||
<div>
|
<div>
|
||||||
<Preview
|
<PublishPreview
|
||||||
dimPreview={this.state.dimPreview}
|
dimPreview={this.state.dimPreview}
|
||||||
file={this.props.file}
|
file={this.props.file}
|
||||||
thumbnail={this.props.thumbnail}
|
thumbnail={this.props.thumbnail}
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
const siteConfig = require('../../config/siteConfig.js');
|
const siteConfig = require('../../config/siteConfig.js');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
analytics: { googleId: googleAnalyticsId },
|
analytics: {
|
||||||
assetDefaults: { thumbnail: defaultThumbnail },
|
googleId: googleAnalyticsId,
|
||||||
details: { title, host },
|
},
|
||||||
|
assetDefaults: {
|
||||||
|
thumbnail: defaultThumbnail,
|
||||||
|
description: defaultDescription,
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
description,
|
||||||
|
host,
|
||||||
|
title,
|
||||||
|
twitter,
|
||||||
|
},
|
||||||
} = siteConfig;
|
} = siteConfig;
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
description,
|
||||||
googleAnalyticsId,
|
googleAnalyticsId,
|
||||||
host,
|
host,
|
||||||
title,
|
title,
|
||||||
defaults: {
|
twitter,
|
||||||
defaultThumbnail,
|
defaultDescription,
|
||||||
},
|
defaultThumbnail,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function (state = initialState, action) {
|
export default function (state = initialState, action) {
|
||||||
|
|
|
@ -1,37 +1,29 @@
|
||||||
const { details: { host } } = require('../../config/siteConfig.js');
|
const createBasicCanonicalLink = (page, siteHost) => {
|
||||||
|
return `${siteHost}/${page}`;
|
||||||
const createBasicCanonicalLink = (page) => {
|
|
||||||
if (!page) {
|
|
||||||
return `${host}`;
|
|
||||||
};
|
|
||||||
return `${host}/${page}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAssetCanonicalLink = (asset) => {
|
const createAssetCanonicalLink = (asset, siteHost) => {
|
||||||
let channelName, certificateId, name, claimId;
|
let channelName, certificateId, name, claimId;
|
||||||
if (asset.claimData) {
|
if (asset.claimData) {
|
||||||
({ channelName, certificateId, name, claimId } = asset.claimData);
|
({ channelName, certificateId, name, claimId } = asset.claimData);
|
||||||
};
|
};
|
||||||
if (channelName) {
|
if (channelName) {
|
||||||
return `${host}/${channelName}:${certificateId}/${name}`;
|
return `${siteHost}/${channelName}:${certificateId}/${name}`;
|
||||||
};
|
};
|
||||||
return `${host}/${claimId}/${name}`;
|
return `${siteHost}/${claimId}/${name}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChannelCanonicalLink = (channel) => {
|
const createChannelCanonicalLink = (channel, siteHost) => {
|
||||||
const { name, longId } = channel;
|
const { name, longId } = channel;
|
||||||
return `${host}/${name}:${longId}`;
|
return `${siteHost}/${name}:${longId}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createCanonicalLink = (asset, channel, page) => {
|
export const createCanonicalLink = (asset, channel, page, siteHost) => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
return createAssetCanonicalLink(asset);
|
return createAssetCanonicalLink(asset, siteHost);
|
||||||
}
|
}
|
||||||
if (channel) {
|
if (channel) {
|
||||||
return createChannelCanonicalLink(channel);
|
return createChannelCanonicalLink(channel, siteHost);
|
||||||
}
|
}
|
||||||
if (page) {
|
return createBasicCanonicalLink(page, siteHost);
|
||||||
return createBasicCanonicalLink(page);
|
|
||||||
}
|
|
||||||
return createBasicCanonicalLink();
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const { details: { title, host, description }, assetDefaults: { thumbnail: defaultThumbnail, description: defaultDescription } } = require('../../config/siteConfig.js');
|
|
||||||
|
|
||||||
const determineOgThumbnailContentType = (thumbnail) => {
|
const determineOgThumbnailContentType = (thumbnail) => {
|
||||||
if (thumbnail) {
|
if (thumbnail) {
|
||||||
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
|
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
|
||||||
|
@ -20,35 +18,35 @@ const determineOgThumbnailContentType = (thumbnail) => {
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const createBasicMetaTags = () => {
|
const createBasicMetaTags = (siteHost, siteDescription, siteTitle, siteTwitter) => {
|
||||||
return [
|
return [
|
||||||
{property: 'og:title', content: title},
|
{property: 'og:title', content: siteTitle},
|
||||||
{property: 'og:url', content: host},
|
{property: 'og:url', content: siteHost},
|
||||||
{property: 'og:site_name', content: title},
|
{property: 'og:site_name', content: siteTitle},
|
||||||
{property: 'og:description', content: description},
|
{property: 'og:description', content: siteDescription},
|
||||||
{property: 'twitter:site', content: '@spee_ch'},
|
{property: 'twitter:site', content: siteTwitter},
|
||||||
{property: 'twitter:card', content: 'summary'},
|
{property: 'twitter:card', content: 'summary'},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChannelMetaTags = (channel) => {
|
const createChannelMetaTags = (siteTitle, siteHost, siteTwitter, channel) => {
|
||||||
const { name, longId } = channel;
|
const { name, longId } = channel;
|
||||||
return [
|
return [
|
||||||
{property: 'og:title', content: `${name} on ${title}`},
|
{property: 'og:title', content: `${name} on ${siteTitle}`},
|
||||||
{property: 'og:url', content: `${host}/${name}:${longId}`},
|
{property: 'og:url', content: `${siteHost}/${name}:${longId}`},
|
||||||
{property: 'og:site_name', content: title},
|
{property: 'og:site_name', content: siteTitle},
|
||||||
{property: 'og:description', content: `${name}, a channel on ${title}`},
|
{property: 'og:description', content: `${name}, a channel on ${siteTitle}`},
|
||||||
{property: 'twitter:site', content: '@spee_ch'},
|
{property: 'twitter:site', content: siteTwitter},
|
||||||
{property: 'twitter:card', content: 'summary'},
|
{property: 'twitter:card', content: 'summary'},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAssetMetaTags = (asset) => {
|
const createAssetMetaTags = (siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail) => {
|
||||||
const { claimData } = asset;
|
const { claimData } = asset;
|
||||||
const { contentType } = claimData;
|
const { contentType } = claimData;
|
||||||
const embedUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
const embedUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
|
||||||
const showUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
const showUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
|
||||||
const source = `${host}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
|
const source = `${siteHost}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
|
||||||
const ogTitle = claimData.title || claimData.name;
|
const ogTitle = claimData.title || claimData.name;
|
||||||
const ogDescription = claimData.description || defaultDescription;
|
const ogDescription = claimData.description || defaultDescription;
|
||||||
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
|
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
|
||||||
|
@ -56,11 +54,11 @@ const createAssetMetaTags = (asset) => {
|
||||||
const metaTags = [
|
const metaTags = [
|
||||||
{property: 'og:title', content: ogTitle},
|
{property: 'og:title', content: ogTitle},
|
||||||
{property: 'og:url', content: showUrl},
|
{property: 'og:url', content: showUrl},
|
||||||
{property: 'og:site_name', content: title},
|
{property: 'og:site_name', content: siteTitle},
|
||||||
{property: 'og:description', content: ogDescription},
|
{property: 'og:description', content: ogDescription},
|
||||||
{property: 'og:image:width', content: 600},
|
{property: 'og:image:width', content: 600},
|
||||||
{property: 'og:image:height', content: 315},
|
{property: 'og:image:height', content: 315},
|
||||||
{property: 'twitter:site', content: '@spee_ch'},
|
{property: 'twitter:site', content: siteTwitter},
|
||||||
];
|
];
|
||||||
if (contentType === 'video/mp4' || contentType === 'video/webm') {
|
if (contentType === 'video/mp4' || contentType === 'video/webm') {
|
||||||
metaTags.push({property: 'og:video', content: source});
|
metaTags.push({property: 'og:video', content: source});
|
||||||
|
@ -85,12 +83,12 @@ const createAssetMetaTags = (asset) => {
|
||||||
return metaTags;
|
return metaTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createMetaTags = (asset, channel) => {
|
export const createMetaTags = (siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail) => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
return createAssetMetaTags(asset);
|
return createAssetMetaTags(siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail);
|
||||||
};
|
};
|
||||||
if (channel) {
|
if (channel) {
|
||||||
return createChannelMetaTags(channel);
|
return createChannelMetaTags(siteHost, siteTitle, siteTwitter, channel);
|
||||||
};
|
};
|
||||||
return createBasicMetaTags();
|
return createBasicMetaTags(siteDescription, siteHost, siteTitle, siteTwitter);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
const { details: { title } } = require('../../config/siteConfig.js');
|
export const createPageTitle = (siteTitle, pageTitle) => {
|
||||||
|
|
||||||
export const createPageTitle = (pageTitle) => {
|
|
||||||
if (!pageTitle) {
|
if (!pageTitle) {
|
||||||
return `${title}`;
|
return `${siteTitle}`;
|
||||||
}
|
}
|
||||||
return `${title} - ${pageTitle}`;
|
return `${siteTitle} - ${pageTitle}`;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue