Skip to content

Commit 4be9c90

Browse files
Merge pull request #5211 from topcoder-platform/seo-fix
Smoke Testing 2020/11/24 - SEO fix
2 parents 4f52467 + 835c7dc commit 4be9c90

File tree

25 files changed

+256
-60
lines changed

25 files changed

+256
-60
lines changed

.circleci/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ workflows:
231231
branches:
232232
only:
233233
- develop
234-
- submission-review
235234
# This is alternate dev env for parallel testing
236235
- "build-test":
237236
context : org-global
@@ -261,7 +260,6 @@ workflows:
261260
branches:
262261
only:
263262
- develop
264-
- submission-review
265263
# Production builds are exectuted
266264
# when PR is merged to the master
267265
# Don't change anything in this configuration

src/shared/components/Contentful/Route.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import _ from 'lodash';
77
import ContentfulLoader from 'containers/ContentfulLoader';
88
import Error404 from 'components/Error404';
99
import LoadingIndicator from 'components/LoadingIndicator';
10-
import { MetaTags } from 'topcoder-react-utils';
10+
import MetaTags from 'components/MetaTags';
1111
import PT from 'prop-types';
1212
import React from 'react';
1313
import { Route, Switch } from 'react-router-dom';

src/shared/components/MetaTags.jsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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);

src/shared/components/Settings/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
import React from 'react';
55
import PT from 'prop-types';
6-
import { MetaTags } from 'topcoder-react-utils';
6+
import MetaTags from 'components/MetaTags';
77

88
import { TABS } from 'actions/page/settings';
99

src/shared/containers/EDU/Home.jsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
import React from 'react';
55
import { config } from 'topcoder-react-utils';
6-
import { Helmet } from 'react-helmet';
6+
import MetaTags from 'components/MetaTags';
77
import Viewport from 'components/Contentful/Viewport';
88
import SearchBar from 'components/Contentful/SearchBar/SearchBar';
99
import { getService } from 'services/contentful';
@@ -44,15 +44,15 @@ export default class EDUHome extends React.Component {
4444

4545
render() {
4646
const { taxonomy } = this.state;
47+
const title = 'Topcoder Thrive | Topcoder Community | Topcoder';
48+
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!';
49+
4750
return (
4851
<div className={homeTheme.container}>
49-
<Helmet>
50-
<title>THRIVE - Grow with us. Tutorials and workshops that matter.</title>
51-
<meta name="title" property="og:title" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
52-
<meta name="description" property="og:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
53-
<meta name="description" property="description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
54-
<meta name="twitter:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
55-
</Helmet>
52+
<MetaTags
53+
description={description}
54+
title={title}
55+
/>
5656
{/* Banner */}
5757
<div className={homeTheme.bannerContainer}>
5858
<div className={homeTheme.bannerImage} />

src/shared/containers/EDU/Search.jsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import _ from 'lodash';
55
import moment from 'moment';
66
import React from 'react';
77
import { config, isomorphy } from 'topcoder-react-utils';
8+
import MetaTags from 'components/MetaTags';
89
import Viewport from 'components/Contentful/Viewport';
910
import SearchBar from 'components/Contentful/SearchBar/SearchBar';
1011
import { getService } from 'services/contentful';
@@ -13,7 +14,6 @@ import { updateQuery } from 'utils/url';
1314
import qs from 'qs';
1415
import LoadingIndicator from 'components/LoadingIndicator';
1516
import SearchPageFilter from 'components/Contentful/SearchPageFilter/SearchPageFilter';
16-
import { Helmet } from 'react-helmet';
1717
// Partials
1818
import ResultTabs from './partials/ResultTabs';
1919
// CSS
@@ -87,18 +87,28 @@ export default class EDUSearch extends React.Component {
8787
const {
8888
taxonomy, query, tree, isShowFilter,
8989
} = this.state;
90+
const title = 'Topcoder Thrive | Topcoder Community | Topcoder';
91+
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!';
92+
93+
const metaTags = (
94+
<MetaTags
95+
description={description}
96+
title={title}
97+
/>
98+
);
9099
// This container needs at least those variables
91100
// to be able to render meaningful data
92-
if (!taxonomy) return <LoadingIndicator />;
101+
if (!taxonomy) {
102+
return (
103+
<React.Fragment>
104+
{ metaTags }
105+
<LoadingIndicator />;
106+
</React.Fragment>
107+
);
108+
}
93109
return (
94110
<div className={searchTheme.container}>
95-
<Helmet>
96-
<title>THRIVE - Search {`${query.title}`}</title>
97-
<meta name="title" property="og:title" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
98-
<meta name="description" property="og:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
99-
<meta name="description" property="description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
100-
<meta name="twitter:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
101-
</Helmet>
111+
{ metaTags }
102112
{/* Banner */}
103113
<div className={searchTheme.bannerContainer}>
104114
<div className={searchTheme.searchBarWrapp}>

src/shared/containers/EDU/Tracks.jsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import _ from 'lodash';
55
import moment from 'moment';
66
import React from 'react';
77
import { config, isomorphy } from 'topcoder-react-utils';
8+
import MetaTags from 'components/MetaTags';
89
import Viewport from 'components/Contentful/Viewport';
910
import SearchBar from 'components/Contentful/SearchBar/SearchBar';
1011
import { getService } from 'services/contentful';
@@ -14,7 +15,6 @@ import qs from 'qs';
1415
import TracksTree from 'components/Contentful/TracksTree/TracksTree';
1516
import LoadingIndicator from 'components/LoadingIndicator';
1617
import TracksFilter from 'components/Contentful/TracksFilter/TracksFilter';
17-
import { Helmet } from 'react-helmet';
1818
// SVGs & Assets
1919
import Dev from 'assets/images/img-development.png';
2020
import Design from 'assets/images/img_design.png';
@@ -163,18 +163,28 @@ export default class EDUTracks extends React.Component {
163163
taxonomy, query, tree, isShowFilter,
164164
articleCnt, videoCnt, forumCnt,
165165
} = this.state;
166+
const title = 'Topcoder Thrive | Topcoder Community | Topcoder';
167+
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!';
168+
169+
const metaTags = (
170+
<MetaTags
171+
description={description}
172+
title={title}
173+
/>
174+
);
166175
// This container needs at least those variables
167176
// to be able to render meaningful data
168-
if (!taxonomy) return <LoadingIndicator />;
177+
if (!taxonomy) {
178+
return (
179+
<React.Fragment>
180+
{ metaTags }
181+
<LoadingIndicator />;
182+
</React.Fragment>
183+
);
184+
}
169185
return (
170186
<div className={tracksTheme.container}>
171-
<Helmet>
172-
<title>THRIVE - {`${query.track}`}</title>
173-
<meta name="title" property="og:title" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
174-
<meta name="description" property="og:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
175-
<meta name="description" property="description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
176-
<meta name="twitter:description" content="THRIVE - Grow with us. Tutorials and workshops that matter." />
177-
</Helmet>
187+
{ metaTags }
178188
{/* Banner */}
179189
<div
180190
className={tracksTheme.bannerContainer}

src/shared/containers/EDU/styles/home.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
.container {
44
display: flex;
5+
flex: 1;
56
flex-direction: column;
6-
overflow: hidden;
7+
width: 100%;
78

89
.bannerContainer {
910
background-image: linear-gradient(98.81deg, #8b41b0 0%, #ef476f 100%);

src/shared/containers/EDU/styles/search.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
.container {
44
display: flex;
5+
flex: 1;
56
flex-direction: column;
6-
overflow: hidden;
7+
width: 100%;
78

89
.bannerContainer {
910
background-color: #2a2a2a;

src/shared/containers/EDU/styles/tracks.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
.container {
44
display: flex;
5+
flex: 1;
56
flex-direction: column;
6-
overflow: hidden;
7+
width: 100%;
78

89
.bannerContainer {
910
min-height: 475px;

src/shared/containers/GigsPages.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ import Viewport from 'components/Contentful/Viewport';
99
import { config } from 'topcoder-react-utils';
1010
import RecruitCRMJobDetails from 'containers/Gigs/RecruitCRMJobDetails';
1111
import { Helmet } from 'react-helmet';
12+
import MetaTags from 'components/MetaTags';
1213

1314

1415
export default function GigsPagesContainer(props) {
1516
const { match } = props;
1617
const { id } = match.params;
1718
const isApply = `${config.GIGS_PAGES_PATH}/${id}/apply` === match.url;
19+
const title = 'Gig Work | Topcoder Community | Topcoder';
20+
const description = 'Compete and build up your profiles and skills! Topcoder members become eligible to work on Gig Work projects by first proving themselves in various skill sets through Topcoder competitions.';
1821
return (
1922
<div>
2023
<Helmet>
21-
<title>Topcoder - Gig Work Opportunities</title>
2224
<script type="text/javascript">{`
2325
window._chatlio = window._chatlio||[];
2426
!function(){ var t=document.getElementById("chatlio-widget-embed");if(t&&window.ChatlioReact&&_chatlio.init)return void _chatlio.init(t,ChatlioReact);for(var e=function(t){return function(){_chatlio.push([t].concat(arguments)) }},i=["configure","identify","track","show","hide","isShown","isOnline", "page", "open", "showOrHide"],a=0;a<i.length;a++)_chatlio[i[a]]||(_chatlio[i[a]]=e(i[a]));var n=document.createElement("script"),c=document.getElementsByTagName("script")[0];n.id="chatlio-widget-embed",n.src="https://w.chatlio.com/w.chatlio-widget.js",n.async=!0,n.setAttribute("data-embed-version","2.3");
@@ -28,6 +30,10 @@ window._chatlio = window._chatlio||[];
2830
`}
2931
</script>
3032
</Helmet>
33+
<MetaTags
34+
description={description}
35+
title={title}
36+
/>
3137
<Header />
3238
{
3339
id ? (

src/shared/containers/Profile.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import PT from 'prop-types';
77
import { connect } from 'react-redux';
88

99
import { actions } from 'topcoder-react-lib';
10-
import { MetaTags } from 'topcoder-react-utils';
10+
import MetaTags from 'components/MetaTags';
1111
import Error404 from 'components/Error404';
1212
import LoadingIndicator from 'components/LoadingIndicator';
1313
import ProfilePage from 'components/ProfilePage';

src/shared/containers/ProfileStats.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Error404 from 'components/Error404';
99
import LoadingIndicator from 'components/LoadingIndicator';
1010
import ProfileStatsPage from 'components/ProfilePage/Stats';
1111
import { shouldShowGraph, isValidTrack } from 'utils/memberStats';
12-
import { MetaTags } from 'topcoder-react-utils';
12+
import MetaTags from 'components/MetaTags';
1313
import _ from 'lodash';
1414
import qs from 'qs';
1515

src/shared/containers/challenge-detail/index.jsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ import {
3939
SUBTRACKS,
4040
CHALLENGE_STATUS,
4141
} from 'utils/tc';
42-
import { config, MetaTags } from 'topcoder-react-utils';
42+
import { config } from 'topcoder-react-utils';
43+
import MetaTags from 'components/MetaTags';
4344
import { actions } from 'topcoder-react-lib';
4445
import { getService } from 'services/contentful';
4546
// import {
@@ -369,10 +370,17 @@ class ChallengeDetailPageContainer extends React.Component {
369370

370371
const isLoggedIn = !_.isEmpty(auth.tokenV3);
371372

373+
const { prizeSets } = challenge;
374+
let challengePrizes = [];
375+
const placementPrizes = _.find(prizeSets, { type: 'placement' });
376+
if (placementPrizes) {
377+
challengePrizes = _.filter(placementPrizes.prizes, p => p.value > 0);
378+
}
379+
372380
/* Generation of data for SEO meta-tags. */
373-
let prizesStr;
374-
if (challenge.prizes && challenge.prizes.length) {
375-
prizesStr = challenge.prizes.map(p => `$${p}`).join('/');
381+
let prizesStr = '';
382+
if (!_.isEmpty(challengePrizes)) {
383+
prizesStr = challengePrizes.map(p => `$${p.value}`).join('/');
376384
prizesStr = `[${prizesStr}] - `;
377385
}
378386
const title = 'Topcoder Challenge | Topcoder Community | Topcoder';
@@ -398,18 +406,10 @@ class ChallengeDetailPageContainer extends React.Component {
398406
hasFirstPlacement = _.some(winners, { placement: 1, handle: userHandle });
399407
}
400408

401-
402409
const submissionEnded = status === CHALLENGE_STATUS.COMPLETED
403410
|| (!_.some(phases, { name: 'Submission', isOpen: true })
404411
&& !_.some(phases, { name: 'Checkpoint Submission', isOpen: true }));
405412

406-
const { prizeSets } = challenge;
407-
let challengePrizes = [];
408-
const placementPrizes = _.find(prizeSets, { type: 'placement' });
409-
if (placementPrizes) {
410-
challengePrizes = placementPrizes.prizes;
411-
}
412-
413413
return (
414414
<div styleName="outer-container">
415415
<div styleName="challenge-detail-container" role="main">

src/shared/containers/challenge-listing/Listing/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import sidebarActions from 'actions/challenge-listing/sidebar';
2525
import communityActions from 'actions/tc-communities';
2626
// import SORT from 'utils/challenge-listing/sort';
2727
import { BUCKETS, filterChanged, sortChangedBucket } from 'utils/challenge-listing/buckets';
28-
import { MetaTags } from 'topcoder-react-utils';
28+
import MetaTags from 'components/MetaTags';
2929
import { USER_GROUP_MAXAGE } from 'config';
3030
import { updateChallengeType } from 'utils/challenge';
3131

0 commit comments

Comments
 (0)