|
| 1 | +/** |
| 2 | + * Auxiliary wrapper around React Helmet that helps to generate meta tags for |
| 3 | + * generic use cases. |
| 4 | + * |
| 5 | + * NOTE: This component relies on `domain` path of Redux store to hold |
| 6 | + * the current app domain, which will serve as the base path for the bundled |
| 7 | + * images. |
| 8 | + */ |
| 9 | + |
| 10 | +import PT from 'prop-types'; |
| 11 | +import React from 'react'; |
| 12 | +import { connect } from 'react-redux'; |
| 13 | +import { Helmet } from 'react-helmet'; |
| 14 | + |
| 15 | +function MetaTags({ |
| 16 | + description, |
| 17 | + domain, |
| 18 | + image, |
| 19 | + siteName, |
| 20 | + socialDescription, |
| 21 | + socialTitle, |
| 22 | + title, |
| 23 | + url, |
| 24 | +}) { |
| 25 | + const img = `${domain}${image}`; |
| 26 | + const socTitle = socialTitle || title; |
| 27 | + const socDesc = socialDescription || description; |
| 28 | + return ( |
| 29 | + <Helmet> |
| 30 | + {/* General tags. */} |
| 31 | + <title> |
| 32 | + {title} |
| 33 | + </title> |
| 34 | + <meta name="description" content={description} /> |
| 35 | + |
| 36 | + {/* Twitter cards. */} |
| 37 | + <meta name="twitter:card" content="summary_large_image" /> |
| 38 | + <meta name="twitter:title" content={socTitle} /> |
| 39 | + <meta name="twitter:description" content={socDesc} /> |
| 40 | + { image ? <meta name="twitter:image" content={img} /> : null } |
| 41 | + { |
| 42 | + siteName ? ( |
| 43 | + <meta name="twitter:site" content={`@${siteName}`} /> |
| 44 | + ) : null |
| 45 | + } |
| 46 | + |
| 47 | + {/* Open Graph data. */} |
| 48 | + <meta property="og:title" content={socTitle} /> |
| 49 | + { image ? <meta property="og:image" content={img} /> : null } |
| 50 | + { image ? <meta property="og:image:alt" content={socTitle} /> : null } |
| 51 | + <meta property="og:description" content={socDesc} /> |
| 52 | + { |
| 53 | + siteName ? (<meta property="og:sitename" content={siteName} />) : null |
| 54 | + } |
| 55 | + { url ? (<meta property="og:url" content={url} />) : null } |
| 56 | + </Helmet> |
| 57 | + ); |
| 58 | +} |
| 59 | + |
| 60 | +MetaTags.defaultProps = { |
| 61 | + image: null, |
| 62 | + siteName: null, |
| 63 | + socialDescription: null, |
| 64 | + socialTitle: null, |
| 65 | + url: null, |
| 66 | +}; |
| 67 | + |
| 68 | +MetaTags.propTypes = { |
| 69 | + description: PT.string.isRequired, |
| 70 | + domain: PT.string.isRequired, |
| 71 | + image: PT.string, |
| 72 | + siteName: PT.string, |
| 73 | + socialDescription: PT.string, |
| 74 | + socialTitle: PT.string, |
| 75 | + title: PT.string.isRequired, |
| 76 | + url: PT.string, |
| 77 | +}; |
| 78 | + |
| 79 | +/* TODO: It is not good to depend on the domain written into redux state here, |
| 80 | + * better pass it via the renderer context at the server side, and get it from |
| 81 | + * the location at the frontend side, or something similar? */ |
| 82 | +export default connect(state => ({ domain: state.domain }))(MetaTags); |
0 commit comments