Skip to content

Commit 6a0bacb

Browse files
Merge pull request #5914 from topcoder-platform/develop
Release v1.14.6
2 parents 1580cba + 23e89c5 commit 6a0bacb

File tree

27 files changed

+508
-49
lines changed

27 files changed

+508
-49
lines changed

.circleci/config.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -357,22 +357,22 @@ workflows:
357357
filters:
358358
branches:
359359
only:
360-
- free
360+
- thrive-rss
361361
# This is beta env for production soft releases
362362
- "build-prod-beta":
363363
context : org-global
364364
filters:
365365
branches:
366366
only:
367-
- free
367+
- mm-leaderboard-theme
368368
# This is stage env for production QA releases
369369
- "build-prod-staging":
370370
context : org-global
371371
filters: &filters-staging
372372
branches:
373373
only:
374374
- develop
375-
- feature/recommended-challenges-update
375+
- ast-timeline-fix
376376
# Production builds are exectuted
377377
# when PR is merged to the master
378378
# Don't change anything in this configuration

automated-smoke-test/page-objects/pages/topcoder/challenge-detail/challenge-detail.helper.ts

-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ export class ChallengeDetailPageHelper {
294294
'Started',
295295
'Registration',
296296
'Submission',
297-
'Review',
298297
'Winners',
299298
];
300299
for (let i = 0; i < childDivs.length; i++) {

config/default.js

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ module.exports = {
110110
HOME: '/home',
111111
BLOG: 'https://www.topcoder-dev.com/blog',
112112
BLOG_FEED: 'https://www.topcoder.com/blog/feed/',
113+
THRIVE_FEED: 'https://topcoder-dev.com/api/feeds/thrive',
113114
COMMUNITY: 'https://community.topcoder-dev.com',
114115
FORUMS: 'https://apps.topcoder-dev.com/forums',
115116
FORUMS_VANILLA: 'https://vanilla.topcoder-dev.com',

config/production.js

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ module.exports = {
5858
CS: 'https://cs.topcoder.com',
5959
},
6060
EMAIL_VERIFY_URL: 'http://www.topcoder.com/settings/account/changeEmail',
61+
THRIVE_FEED: 'https://topcoder.com/api/feeds/thrive',
6162
},
6263
/* Filestack configuration for uploading Submissions
6364
* These are for the production back end */

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
"redux-promise": "^0.6.0",
141141
"request-ip": "^2.0.2",
142142
"require-context": "^1.1.0",
143+
"rss": "^1.2.2",
143144
"rss-parser": "^3.12.0",
144145
"serialize-javascript": "^2.1.1",
145146
"serve-favicon": "^2.5.0",

src/server/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import mmLeaderboardRouter from './routes/mmLeaderboard';
3131
import growsurfRouter from './routes/growsurf';
3232
import gSheetsRouter from './routes/gSheet';
3333
import blogRouter from './routes/blog';
34+
import feedsRouter from './routes/feeds';
3435

3536
/* Dome API for topcoder communities */
3637
import tcCommunitiesDemoApi from './tc-communities';
@@ -143,6 +144,7 @@ async function onExpressJsSetup(server) {
143144
server.use('/api/growsurf', growsurfRouter);
144145
server.use('/api/gsheets', gSheetsRouter);
145146
server.use('/api/blog', blogRouter);
147+
server.use('/api/feeds', feedsRouter);
146148

147149
// serve demo api
148150
server.use(

src/server/routes/feeds.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* The routes that expose assets and content from Contentful CMS to the CDN.
3+
*/
4+
5+
import express from 'express';
6+
import RSS from 'rss';
7+
import ReactDOMServer from 'react-dom/server';
8+
import md from 'utils/markdown';
9+
import {
10+
getService,
11+
} from '../services/contentful';
12+
13+
const cors = require('cors');
14+
15+
const routes = express.Router();
16+
17+
// Enables CORS on those routes according config above
18+
// ToDo configure CORS for set of our trusted domains
19+
routes.use(cors());
20+
routes.options('*', cors());
21+
22+
routes.get('/thrive', async (req, res, next) => {
23+
try {
24+
const data = await getService('EDU', 'master', true).queryEntries({
25+
content_type: 'article',
26+
limit: 20,
27+
order: '-sys.createdAt',
28+
include: 2,
29+
});
30+
const feed = new RSS({
31+
title: 'Topcoder Thrive',
32+
description: 'Tutorials And Workshops That Matter | Thrive | Topcoder',
33+
feed_url: 'https://topcoder.com/api/feeds/thrive',
34+
site_url: 'https://topcoder.com/thrive',
35+
image_url: 'https://www.topcoder.com/wp-content/uploads/2020/05/cropped-TC-Icon-32x32.png',
36+
docs: 'https://www.topcoder.com/thrive/tracks?track=Topcoder',
37+
webMaster: '<[email protected]> Kiril Kartunov',
38+
copyright: '2021 - today, Topcoder',
39+
language: 'en',
40+
categories: ['Competitive Programming', 'Data Science', 'Design', 'Development', 'QA', 'Gig work', 'Topcoder'],
41+
ttl: '60',
42+
});
43+
if (data && data.total) {
44+
data.items.forEach((entry) => {
45+
feed.item({
46+
title: entry.fields.title,
47+
description: ReactDOMServer.renderToString(md(entry.fields.content)),
48+
url: `https://topcoder.com/thrive/articles/${entry.fields.slug || encodeURIComponent(entry.fields.title)}?utm_source=community&utm_campaign=thrive-feed&utm_medium=promotion`,
49+
date: entry.fields.creationDate,
50+
categories: entry.fields.tags,
51+
author: entry.fields.contentAuthor[0].fields.name,
52+
});
53+
});
54+
}
55+
res.set('Content-Type', 'application/rss+xml');
56+
res.send(feed.xml({ indent: true }));
57+
} catch (e) {
58+
next(e);
59+
}
60+
});
61+
62+
export default routes;

src/shared/actions/mmLeaderboard.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { redux } from 'topcoder-react-utils';
1+
import { redux, config } from 'topcoder-react-utils';
22
import Service from 'services/mmLeaderboard';
33
import _ from 'lodash';
44

5+
56
/**
67
* Fetch init
78
*/
@@ -34,11 +35,22 @@ async function getMMLeaderboardDone(id) {
3435
score: scores && scores.length ? scores[0].score : '...',
3536
});
3637
});
37-
data = _.orderBy(data, [d => (Number(d.score) ? Number(d.score) : 0)], ['desc']).map((r, i) => ({
38+
data = _.orderBy(data, [d => (Number(d.score) ? Number(d.score) : 0), d => new Date(d.updated) - new Date()], ['desc']).map((r, i) => ({
3839
...r,
3940
rank: i + 1,
4041
score: r.score % 1 ? Number(r.score).toFixed(5) : r.score,
4142
}));
43+
// Fetch member photos and rating for top 10
44+
const results = await Promise.all(
45+
_.take(data, 10).map(d => fetch(`${config.API.V5}/members/${d.createdBy}`)),
46+
);
47+
const memberData = await Promise.all(results.map(r => r.json()));
48+
// merge with data
49+
// eslint-disable-next-line array-callback-return
50+
memberData.map((member, indx) => {
51+
data[indx].photoUrl = member.photoURL;
52+
data[indx].rating = member.maxRating && member.maxRating.rating;
53+
});
4254
}
4355
return {
4456
id,

src/shared/components/InputSelect/index.jsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export default class InputSelect extends Component {
111111
let i = 0;
112112
let node = e.target;
113113
const REG = new RegExp(_id);
114-
while (node && i < 5) {
114+
while (node && i < 20) {
115115
if (REG.test(node.className)) {
116116
return true;
117117
}
@@ -131,6 +131,7 @@ export default class InputSelect extends Component {
131131
placeholder,
132132
labelKey,
133133
options,
134+
onKeyPress,
134135
} = this.props;
135136

136137
const {
@@ -139,9 +140,10 @@ export default class InputSelect extends Component {
139140
filterVal,
140141
} = this.state;
141142

143+
const escapeRegExp = stringToGoIntoTheRegex => stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); /* eslint-disable-line no-useless-escape */
142144
let fiterList = options;
143145
if (filterVal) {
144-
const REG = new RegExp(filterVal, 'i');
146+
const REG = new RegExp(escapeRegExp(filterVal), 'i');
145147
fiterList = filter(options, o => REG.test(o[labelKey]));
146148
}
147149
const list = map(fiterList, o => (
@@ -171,7 +173,7 @@ export default class InputSelect extends Component {
171173
<div styleName="modal">
172174
<div styleName="modal-input-container">
173175

174-
<input type="text" onChange={this.onFilterChange} placeholder="Search" />
176+
<input type="text" onChange={this.onFilterChange} placeholder="Search" onKeyPress={onKeyPress} />
175177
</div>
176178
<div styleName="modal-list-container" onScroll={this.onLoadMore}>
177179
{list}
@@ -195,6 +197,7 @@ InputSelect.defaultProps = {
195197
isLoading: false,
196198
onChange: () => {},
197199
onLoadMore: () => {},
200+
onKeyPress: () => {},
198201
};
199202

200203
InputSelect.propTypes = {
@@ -205,6 +208,7 @@ InputSelect.propTypes = {
205208
placeholder: PT.string,
206209
onChange: PT.func,
207210
onLoadMore: PT.func,
211+
onKeyPress: PT.func,
208212
hasMore: PT.bool,
209213
isLoading: PT.bool,
210214
disabled: PT.bool,

0 commit comments

Comments
 (0)