diff --git a/src/shared/components/Contentful/Route.jsx b/src/shared/components/Contentful/Route.jsx index 18c23943f8..cce6a728f4 100644 --- a/src/shared/components/Contentful/Route.jsx +++ b/src/shared/components/Contentful/Route.jsx @@ -7,7 +7,7 @@ import _ from 'lodash'; import ContentfulLoader from 'containers/ContentfulLoader'; import Error404 from 'components/Error404'; import LoadingIndicator from 'components/LoadingIndicator'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import PT from 'prop-types'; import React from 'react'; import { Route, Switch } from 'react-router-dom'; diff --git a/src/shared/components/MetaTags.jsx b/src/shared/components/MetaTags.jsx new file mode 100644 index 0000000000..bca2314f22 --- /dev/null +++ b/src/shared/components/MetaTags.jsx @@ -0,0 +1,82 @@ +/** + * Auxiliary wrapper around React Helmet that helps to generate meta tags for + * generic use cases. + * + * NOTE: This component relies on `domain` path of Redux store to hold + * the current app domain, which will serve as the base path for the bundled + * images. + */ + +import PT from 'prop-types'; +import React from 'react'; +import { connect } from 'react-redux'; +import { Helmet } from 'react-helmet'; + +function MetaTags({ + description, + domain, + image, + siteName, + socialDescription, + socialTitle, + title, + url, +}) { + const img = `${domain}${image}`; + const socTitle = socialTitle || title; + const socDesc = socialDescription || description; + return ( + + {/* General tags. */} + + {title} + + + + {/* Twitter cards. */} + + + + { image ? : null } + { + siteName ? ( + + ) : null + } + + {/* Open Graph data. */} + + { image ? : null } + { image ? : null } + + { + siteName ? () : null + } + { url ? () : null } + + ); +} + +MetaTags.defaultProps = { + image: null, + siteName: null, + socialDescription: null, + socialTitle: null, + url: null, +}; + +MetaTags.propTypes = { + description: PT.string.isRequired, + domain: PT.string.isRequired, + image: PT.string, + siteName: PT.string, + socialDescription: PT.string, + socialTitle: PT.string, + title: PT.string.isRequired, + url: PT.string, +}; + +/* TODO: It is not good to depend on the domain written into redux state here, + * better pass it via the renderer context at the server side, and get it from + * the location at the frontend side, or something similar? */ +export default connect(state => ({ domain: state.domain }))(MetaTags); diff --git a/src/shared/components/Settings/index.jsx b/src/shared/components/Settings/index.jsx index f535d39618..9585bb8a31 100644 --- a/src/shared/components/Settings/index.jsx +++ b/src/shared/components/Settings/index.jsx @@ -3,7 +3,7 @@ */ import React from 'react'; import PT from 'prop-types'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import { TABS } from 'actions/page/settings'; diff --git a/src/shared/containers/EDU/Home.jsx b/src/shared/containers/EDU/Home.jsx index 30c8455a26..6ec731d03e 100644 --- a/src/shared/containers/EDU/Home.jsx +++ b/src/shared/containers/EDU/Home.jsx @@ -3,7 +3,7 @@ */ import React from 'react'; import { config } from 'topcoder-react-utils'; -import { Helmet } from 'react-helmet'; +import MetaTags from 'components/MetaTags'; import Viewport from 'components/Contentful/Viewport'; import SearchBar from 'components/Contentful/SearchBar/SearchBar'; import { getService } from 'services/contentful'; @@ -44,15 +44,15 @@ export default class EDUHome extends React.Component { render() { const { taxonomy } = this.state; + const title = 'Topcoder Thrive | Topcoder Community | Topcoder'; + const description = 'Thrive is our vault of content that we have been gathering over the years. It is full of tutorials and workshops that matter. Grow with us!'; + return (
- - THRIVE - Grow with us. Tutorials and workshops that matter. - - - - - + {/* Banner */}
diff --git a/src/shared/containers/EDU/Search.jsx b/src/shared/containers/EDU/Search.jsx index 6b311aa68e..b16fcfdf12 100644 --- a/src/shared/containers/EDU/Search.jsx +++ b/src/shared/containers/EDU/Search.jsx @@ -5,6 +5,7 @@ import _ from 'lodash'; import moment from 'moment'; import React from 'react'; import { config, isomorphy } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import Viewport from 'components/Contentful/Viewport'; import SearchBar from 'components/Contentful/SearchBar/SearchBar'; import { getService } from 'services/contentful'; @@ -13,7 +14,6 @@ import { updateQuery } from 'utils/url'; import qs from 'qs'; import LoadingIndicator from 'components/LoadingIndicator'; import SearchPageFilter from 'components/Contentful/SearchPageFilter/SearchPageFilter'; -import { Helmet } from 'react-helmet'; // Partials import ResultTabs from './partials/ResultTabs'; // CSS @@ -87,18 +87,28 @@ export default class EDUSearch extends React.Component { const { taxonomy, query, tree, isShowFilter, } = this.state; + const title = 'Topcoder Thrive | Topcoder Community | Topcoder'; + const description = 'Thrive is our vault of content that we have been gathering over the years. It is full of tutorials and workshops that matter. Grow with us!'; + + const metaTags = ( + + ); // This container needs at least those variables // to be able to render meaningful data - if (!taxonomy) return ; + if (!taxonomy) { + return ( + + { metaTags } + ; + + ); + } return (
- - THRIVE - Search {`${query.title}`} - - - - - + { metaTags } {/* Banner */}
diff --git a/src/shared/containers/EDU/Tracks.jsx b/src/shared/containers/EDU/Tracks.jsx index d98099191e..8ede19d331 100644 --- a/src/shared/containers/EDU/Tracks.jsx +++ b/src/shared/containers/EDU/Tracks.jsx @@ -5,6 +5,7 @@ import _ from 'lodash'; import moment from 'moment'; import React from 'react'; import { config, isomorphy } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import Viewport from 'components/Contentful/Viewport'; import SearchBar from 'components/Contentful/SearchBar/SearchBar'; import { getService } from 'services/contentful'; @@ -14,7 +15,6 @@ import qs from 'qs'; import TracksTree from 'components/Contentful/TracksTree/TracksTree'; import LoadingIndicator from 'components/LoadingIndicator'; import TracksFilter from 'components/Contentful/TracksFilter/TracksFilter'; -import { Helmet } from 'react-helmet'; // SVGs & Assets import Dev from 'assets/images/img-development.png'; import Design from 'assets/images/img_design.png'; @@ -163,18 +163,28 @@ export default class EDUTracks extends React.Component { taxonomy, query, tree, isShowFilter, articleCnt, videoCnt, forumCnt, } = this.state; + const title = 'Topcoder Thrive | Topcoder Community | Topcoder'; + const description = 'Thrive is our vault of content that we have been gathering over the years. It is full of tutorials and workshops that matter. Grow with us!'; + + const metaTags = ( + + ); // This container needs at least those variables // to be able to render meaningful data - if (!taxonomy) return ; + if (!taxonomy) { + return ( + + { metaTags } + ; + + ); + } return (
- - THRIVE - {`${query.track}`} - - - - - + { metaTags } {/* Banner */}
- Topcoder - Gig Work Opportunities +
{ id ? ( diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx index 90a95b91f6..8c12c66334 100644 --- a/src/shared/containers/Profile.jsx +++ b/src/shared/containers/Profile.jsx @@ -7,7 +7,7 @@ import PT from 'prop-types'; import { connect } from 'react-redux'; import { actions } from 'topcoder-react-lib'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import Error404 from 'components/Error404'; import LoadingIndicator from 'components/LoadingIndicator'; import ProfilePage from 'components/ProfilePage'; diff --git a/src/shared/containers/ProfileStats.jsx b/src/shared/containers/ProfileStats.jsx index 561ab79d9c..259e95865f 100644 --- a/src/shared/containers/ProfileStats.jsx +++ b/src/shared/containers/ProfileStats.jsx @@ -9,7 +9,7 @@ import Error404 from 'components/Error404'; import LoadingIndicator from 'components/LoadingIndicator'; import ProfileStatsPage from 'components/ProfilePage/Stats'; import { shouldShowGraph, isValidTrack } from 'utils/memberStats'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import _ from 'lodash'; import qs from 'qs'; diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx index d088940562..ddbb35160e 100644 --- a/src/shared/containers/challenge-detail/index.jsx +++ b/src/shared/containers/challenge-detail/index.jsx @@ -39,7 +39,8 @@ import { SUBTRACKS, CHALLENGE_STATUS, } from 'utils/tc'; -import { config, MetaTags } from 'topcoder-react-utils'; +import { config } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import { actions } from 'topcoder-react-lib'; import { getService } from 'services/contentful'; // import { diff --git a/src/shared/containers/challenge-listing/Listing/index.jsx b/src/shared/containers/challenge-listing/Listing/index.jsx index ab8c52a856..77104cb8e6 100644 --- a/src/shared/containers/challenge-listing/Listing/index.jsx +++ b/src/shared/containers/challenge-listing/Listing/index.jsx @@ -25,7 +25,7 @@ import sidebarActions from 'actions/challenge-listing/sidebar'; import communityActions from 'actions/tc-communities'; // import SORT from 'utils/challenge-listing/sort'; import { BUCKETS, filterChanged, sortChangedBucket } from 'utils/challenge-listing/buckets'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import { USER_GROUP_MAXAGE } from 'config'; import { updateChallengeType } from 'utils/challenge'; diff --git a/src/shared/containers/terms-detail/index.jsx b/src/shared/containers/terms-detail/index.jsx index af5efa5ab0..66755806ec 100644 --- a/src/shared/containers/terms-detail/index.jsx +++ b/src/shared/containers/terms-detail/index.jsx @@ -11,7 +11,7 @@ import _ from 'lodash'; import LoadingIndicator from 'components/LoadingIndicator'; import TermDetails from 'components/Terms/TermDetails'; import { actions } from 'topcoder-react-lib'; -import { MetaTags } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import { Modal, PrimaryButton } from 'topcoder-react-ui-kit'; import SwitchWithLabel from 'components/SwitchWithLabel'; import { themr } from 'react-css-super-themr'; diff --git a/src/shared/routes/Communities/Blockchain/Routes.jsx b/src/shared/routes/Communities/Blockchain/Routes.jsx index 6ede568403..e25565ef27 100644 --- a/src/shared/routes/Communities/Blockchain/Routes.jsx +++ b/src/shared/routes/Communities/Blockchain/Routes.jsx @@ -27,7 +27,8 @@ import Settings from 'routes/Settings'; import TermsDetail from 'routes/TermsDetail'; import { Route, Switch } from 'react-router-dom'; import { ThemeProvider } from 'react-css-super-themr'; -import { config, MetaTags } from 'topcoder-react-utils'; +import { config } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import primaryButtonStyle from 'components/buttons/outline/round/open-sans/green-uppercase.scss'; import secondaryButtonStyle from 'components/buttons/outline/round/open-sans/blue-uppercase.scss'; diff --git a/src/shared/routes/Communities/Cognitive/Routes.jsx b/src/shared/routes/Communities/Cognitive/Routes.jsx index 1e82a637f8..df8e3f2854 100644 --- a/src/shared/routes/Communities/Cognitive/Routes.jsx +++ b/src/shared/routes/Communities/Cognitive/Routes.jsx @@ -23,7 +23,8 @@ import socialImage from 'assets/images/communities/cognitive/social.jpg'; import TermsDetail from 'routes/TermsDetail'; import { Route, Switch } from 'react-router-dom'; -import { config, MetaTags } from 'topcoder-react-utils'; +import { config } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; export default function Cognitive({ base, member, meta }) { return ( diff --git a/src/shared/routes/Communities/Mobile/Routes.jsx b/src/shared/routes/Communities/Mobile/Routes.jsx index 92a94cb724..8a5697d40a 100644 --- a/src/shared/routes/Communities/Mobile/Routes.jsx +++ b/src/shared/routes/Communities/Mobile/Routes.jsx @@ -17,7 +17,8 @@ import ProfileStats from 'routes/ProfileStats'; import { Route, Switch } from 'react-router-dom'; import { ThemeProvider } from 'react-css-super-themr'; -import { config, MetaTags } from 'topcoder-react-utils'; +import { config } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import primaryButtonStyle from 'components/buttons/outline/round/open-sans/green-uppercase.scss'; import secondaryButtonStyle from 'components/buttons/outline/round/open-sans/blue-uppercase.scss'; diff --git a/src/shared/routes/EDUHome.jsx b/src/shared/routes/EDUHome.jsx new file mode 100644 index 0000000000..ef5db510aa --- /dev/null +++ b/src/shared/routes/EDUHome.jsx @@ -0,0 +1,26 @@ +/** + * The loader of EDUHome page webpack chunks. + */ +import path from 'path'; +import React from 'react'; +import LoadingPagePlaceholder from 'components/LoadingPagePlaceholder'; +import { AppChunk, webpack } from 'topcoder-react-utils'; + +export default function EDUHome(props) { + return ( + import(/* webpackChunkName: "eduHome/chunk" */ 'containers/EDU/Home') + .then(({ default: Home }) => ( + + )) + } + renderPlaceholder={() => } + renderServer={() => { + const p = webpack.resolveWeak('containers/EDU/Home'); + const Home = webpack.requireWeak(path.resolve(__dirname, p)); + return ; + }} + /> + ); +} diff --git a/src/shared/routes/EDUSearch.jsx b/src/shared/routes/EDUSearch.jsx new file mode 100644 index 0000000000..be5be4dcfe --- /dev/null +++ b/src/shared/routes/EDUSearch.jsx @@ -0,0 +1,26 @@ +/** + * The loader of EDUSearch page webpack chunks. + */ +import path from 'path'; +import React from 'react'; +import LoadingPagePlaceholder from 'components/LoadingPagePlaceholder'; +import { AppChunk, webpack } from 'topcoder-react-utils'; + +export default function EDUSearch(props) { + return ( + import(/* webpackChunkName: "eduSearch/chunk" */ 'containers/EDU/Search') + .then(({ default: Search }) => ( + + )) + } + renderPlaceholder={() => } + renderServer={() => { + const p = webpack.resolveWeak('containers/EDU/Search'); + const Search = webpack.requireWeak(path.resolve(__dirname, p)); + return ; + }} + /> + ); +} diff --git a/src/shared/routes/EDUTracks.jsx b/src/shared/routes/EDUTracks.jsx new file mode 100644 index 0000000000..2b0c3565d0 --- /dev/null +++ b/src/shared/routes/EDUTracks.jsx @@ -0,0 +1,26 @@ +/** + * The loader of EDUTracks page webpack chunks. + */ +import path from 'path'; +import React from 'react'; +import LoadingPagePlaceholder from 'components/LoadingPagePlaceholder'; +import { AppChunk, webpack } from 'topcoder-react-utils'; + +export default function EDUTracks(props) { + return ( + import(/* webpackChunkName: "eduTracks/chunk" */ 'containers/EDU/Tracks') + .then(({ default: Tracks }) => ( + + )) + } + renderPlaceholder={() => } + renderServer={() => { + const p = webpack.resolveWeak('containers/EDU/Tracks'); + const Tracks = webpack.requireWeak(path.resolve(__dirname, p)); + return ; + }} + /> + ); +} diff --git a/src/shared/routes/GigsPages.jsx b/src/shared/routes/GigsPages.jsx index dca924c079..b822b9ab69 100644 --- a/src/shared/routes/GigsPages.jsx +++ b/src/shared/routes/GigsPages.jsx @@ -1,21 +1,26 @@ /** * The loader of Gigs page webpack chunks. */ +import path from 'path'; import React from 'react'; - import LoadingPagePlaceholder from 'components/LoadingPagePlaceholder'; -import { AppChunk } from 'topcoder-react-utils'; +import { AppChunk, webpack } from 'topcoder-react-utils'; export default function GigsPagesRoute(props) { return ( import(/* webpackChunkName: "gigsPages/chunk" */ 'containers/GigsPages') .then(({ default: GigsPagesContainer }) => ( )) } renderPlaceholder={() => } + renderServer={() => { + const p = webpack.resolveWeak('containers/GigsPages'); + const GigsPagesContainer = webpack.requireWeak(path.resolve(__dirname, p)); + return ; + }} /> ); } diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx index 91904b8d53..2c8cd97267 100644 --- a/src/shared/routes/Topcoder/Routes.jsx +++ b/src/shared/routes/Topcoder/Routes.jsx @@ -22,9 +22,9 @@ import ContentfulLoader from 'containers/ContentfulLoader'; import LoadingIndicator from 'components/LoadingIndicator'; import Article from 'components/Contentful/Article'; -import EDUHome from 'containers/EDU/Home'; -import EDUTracks from 'containers/EDU/Tracks'; -import EDUSearch from 'containers/EDU/Search'; +import EDUHome from '../EDUHome'; +import EDUTracks from '../EDUTracks'; +import EDUSearch from '../EDUSearch'; import ChallengeListing from './ChallengeListing'; import Dashboard from './Dashboard'; import Notifications from './Notifications'; diff --git a/src/shared/routes/index.jsx b/src/shared/routes/index.jsx index a69a461bf4..385172a9bc 100644 --- a/src/shared/routes/index.jsx +++ b/src/shared/routes/index.jsx @@ -11,7 +11,8 @@ import React from 'react'; import { Switch, Route, withRouter, Redirect, } from 'react-router-dom'; -import { MetaTags, config } from 'topcoder-react-utils'; +import { config } from 'topcoder-react-utils'; +import MetaTags from 'components/MetaTags'; import PT from 'prop-types';