Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .exchange-rates.cache
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"disclaimer":"Usage subject to terms: https://openexchangerates.org/terms","license":"https://openexchangerates.org/license","timestamp":1505811600,"base":"USD","rates":{"AED":3.673014,"AFN":68.614,"ALL":111.776085,"AMD":478.295,"ANG":1.77968,"AOA":165.9215,"ARS":17.0995,"AUD":1.252216,"AWG":1.796504,"AZN":1.6855,"BAM":1.635351,"BBD":2,"BDT":80.708351,"BGN":1.63299,"BHD":0.377284,"BIF":1745.45,"BMD":1,"BND":1.348334,"BOB":6.963698,"BRL":3.13655,"BSD":1,"BTC":0.000253137519,"BTN":64.166558,"BWP":10.15073,"BYN":1.938191,"BZD":2.015375,"CAD":1.228379,"CDF":1562.881563,"CHF":0.961344,"CLF":0.02325,"CLP":624.6,"CNH":6.585748,"CNY":6.583106,"COP":2909,"CRC":576.745,"CUC":1,"CUP":25.5,"CVE":92.55,"CZK":21.791915,"DJF":178.57,"DKK":6.209958,"DOP":47.241072,"DZD":111.282333,"EGP":17.6487,"ERN":15.331922,"ETB":23.566647,"EUR":0.834449,"FJD":2.012549,"FKP":0.742007,"GBP":0.742007,"GEL":2.4717,"GGP":0.742007,"GHS":4.381672,"GIP":0.742007,"GMD":45.95,"GNF":8960.5,"GTQ":7.292123,"GYD":207.92,"HKD":7.803084,"HNL":23.385811,"HRK":6.235527,"HTG":63.0095,"HUF":258.117002,"IDR":13264.557907,"ILS":3.52281,"IMP":0.742007,"INR":64.245,"IQD":1165.609957,"IRR":33337.5,"ISK":105.94,"JEP":0.742007,"JMD":130.865,"JOD":0.709001,"JPY":111.5666875,"KES":103.367854,"KGS":68.455728,"KHR":4058.3,"KMF":412.561444,"KPW":900,"KRW":1131.54,"KWD":0.301307,"KYD":0.833119,"KZT":340.305898,"LAK":8284.05,"LBP":1507.65,"LKR":153.101598,"LRD":117.111232,"LSL":13.182434,"LYD":1.361748,"MAD":9.333026,"MDL":17.669492,"MGA":2989.5,"MKD":51.401508,"MMK":1345.8,"MNT":2468.97015,"MOP":8.049209,"MRO":364.935,"MUR":33.4205,"MVR":15.450233,"MWK":724.527458,"MXN":17.810955,"MYR":4.191489,"MZN":61.732957,"NAD":13.182434,"NGN":360.37226,"NIO":30.359705,"NOK":7.804146,"NPR":102.548085,"NZD":1.372687,"OMR":0.385015,"PAB":1,"PEN":3.247336,"PGK":3.23879,"PHP":50.99,"PKR":105.333422,"PLN":3.5831,"PYG":5658.3,"QAR":3.656908,"RON":3.840574,"RSD":99.257652,"RUB":58.1634,"RWF":831.55,"SAR":3.75035,"SBD":7.823231,"SCR":13.55,"SDG":6.675602,"SEK":7.95019,"SGD":1.348793,"SHP":0.742007,"SLL":7558.984779,"SOS":578.446731,"SRD":7.438,"SSP":126.2714,"STD":20487.565958,"SVC":8.747714,"SYP":515.00499,"SZL":13.187884,"THB":33.083,"TJS":8.795919,"TMT":3.50998,"TND":2.437494,"TOP":2.2232,"TRY":3.501853,"TTD":6.74491,"TWD":30.156,"TZS":2243.2,"UAH":26.155965,"UGX":3595.3,"USD":1,"UYU":28.986914,"UZS":8090.5,"VEF":9.995002,"VND":22731.335859,"VUV":104.480024,"WST":2.480432,"XAF":547.362622,"XAG":0.05821916,"XAU":0.00076407,"XCD":2.70255,"XDR":0.702671,"XOF":547.362622,"XPD":0.00107407,"XPF":99.576246,"XPT":0.00104276,"YER":250.344142,"ZAR":13.330636,"ZMW":9.633257,"ZWL":322.355011}}
{"disclaimer":"Usage subject to terms: https://openexchangerates.org/terms","license":"https://openexchangerates.org/license","timestamp":1506333600,"base":"USD","rates":{"AED":3.673158,"AFN":68.6465,"ALL":112.58,"AMD":479.63649,"ANG":1.785999,"AOA":165.9215,"ARS":17.3005,"AUD":1.25734,"AWG":1.794996,"AZN":1.7,"BAM":1.639912,"BBD":2,"BDT":82.238586,"BGN":1.644233,"BHD":0.377199,"BIF":1754.926108,"BMD":1,"BND":1.349018,"BOB":6.998006,"BRL":3.125,"BSD":1,"BTC":0.000265269665,"BTN":64.934734,"BWP":10.197008,"BYN":1.942719,"BZD":2.022299,"CAD":1.232918,"CDF":1562.881563,"CHF":0.973363,"CLF":0.02322,"CLP":627.82,"CNH":6.614113,"CNY":6.621016,"COP":2905.8,"CRC":576.881761,"CUC":1,"CUP":25.5,"CVE":92.675,"CZK":21.899438,"DJF":178.57,"DKK":6.259235,"DOP":47.311859,"DZD":112.421483,"EGP":17.668,"ERN":15.340012,"ETB":23.42892,"EUR":0.841295,"FJD":2.011699,"FKP":0.739307,"GBP":0.739307,"GEL":2.4777,"GGP":0.739307,"GHS":4.4175,"GIP":0.739307,"GMD":45.97,"GNF":8979.55,"GTQ":7.319581,"GYD":206.814115,"HKD":7.812934,"HNL":23.470563,"HRK":6.2963,"HTG":63.622543,"HUF":261.175784,"IDR":13329.280094,"ILS":3.51128,"IMP":0.739307,"INR":65.09,"IQD":1170.400369,"IRR":33402.5,"ISK":107.805414,"JEP":0.739307,"JMD":130.83531,"JOD":0.709101,"JPY":112.0615,"KES":103.225,"KGS":68.451752,"KHR":4077.641667,"KMF":412.6,"KPW":900,"KRW":1131.7025,"KWD":0.301675,"KYD":0.836119,"KZT":342.36841,"LAK":8310.85,"LBP":1508.862443,"LKR":153.01,"LRD":117.254872,"LSL":13.248849,"LYD":1.360769,"MAD":9.37926,"MDL":17.639888,"MGA":3056.925758,"MKD":51.812066,"MMK":1368.61006,"MNT":2460.413333,"MOP":8.070913,"MRO":365.964861,"MUR":33.475,"MVR":15.409873,"MWK":725.52,"MXN":17.795665,"MYR":4.198771,"MZN":62.012392,"NAD":13.248849,"NGN":358.522572,"NIO":30.342742,"NOK":7.823037,"NPR":103.982045,"NZD":1.37505,"OMR":0.38502,"PAB":1,"PEN":3.246393,"PGK":3.205299,"PHP":50.6885,"PKR":105.715129,"PLN":3.592565,"PYG":5707.227124,"QAR":3.65395,"RON":3.868876,"RSD":100.579362,"RUB":57.3153,"RWF":834.26744,"SAR":3.7502,"SBD":7.746542,"SCR":13.150523,"SDG":6.699279,"SEK":8.025323,"SGD":1.350376,"SHP":0.739307,"SLL":7550,"SOS":580.12834,"SRD":7.438,"SSP":126.6773,"STD":20559.200196,"SVC":8.778896,"SYP":514.98999,"SZL":13.24451,"THB":33.12,"TJS":8.839108,"TMT":3.499986,"TND":2.452101,"TOP":2.21298,"TRY":3.518075,"TTD":6.798325,"TWD":30.224452,"TZS":2244.2,"UAH":26.338725,"UGX":3637.566138,"USD":1,"UYU":28.969536,"UZS":8109.75,"VEF":9.985022,"VND":22745.133333,"VUV":104.150397,"WST":2.491006,"XAF":551.853344,"XAG":0.05893621,"XAU":0.00077184,"XCD":2.70255,"XDR":0.702884,"XOF":551.853344,"XPD":0.0010835,"XPF":100.393198,"XPT":0.00106785,"YER":250.25,"ZAR":13.26789,"ZMW":9.536229,"ZWL":322.355011}}
2 changes: 1 addition & 1 deletion __tests__/shared/actions/tc-communities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import actions from 'actions/tc-communities/index';
describe('tcCommunity.joinDone at frontend with 404 response', () => {
global.fetch = jest.fn(() => Promise.resolve({
ok: true,
json: () => ({
json: () => Promise.resolve({
result: {
status: 200,
content: 'dummy',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exports[`Snapshot match 1`] = `
<img
alt="preview"
className="src-shared-components-SubmissionManagement-Submission-___styles__dev-img___rbFEF"
src="https://studio.topcoder-dev.com?module=DownloadSubmission&sbmid=undefined&sbt=tiny"
src="https://studio.topcoder-dev.com?module=DownloadSubmission&sbmid=undefined&sbt=tiny&sfi=1"
/>
</td>
<td
Expand Down Expand Up @@ -58,7 +58,7 @@ exports[`Snapshot match 2`] = `
<img
alt="preview"
className="src-shared-components-SubmissionManagement-Submission-___styles__dev-img___rbFEF"
src="https://studio.topcoder-dev.com?module=DownloadSubmission&sbmid=12345&sbt=tiny"
src="https://studio.topcoder-dev.com?module=DownloadSubmission&sbmid=12345&sbt=tiny&sfi=1"
/>
</td>
<td
Expand Down
1 change: 1 addition & 0 deletions __tests__/shared/components/__snapshots__/Modal.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports[`Snapshot match 1`] = `
Object {
"container": "src-shared-components-Modal-___styles__container___2HN2G",
"overlay": "src-shared-components-Modal-___styles__overlay___36ZAX",
"scrolling-disabled-by-modal": "src-shared-components-Modal-___styles__scrolling-disabled-by-modal___1YgZ3",
}
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ describe('full render connnected component and dispatch actions', () => {
},
tokenV3: 'tokenV3',
},
groups: {
groups: {},
},
};

const mockStore = configureStore();
Expand Down
3 changes: 3 additions & 0 deletions docs/coding-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [SCSS](#basics-scss)
- [Unit Tests](#basics-unit-tests)
- [Code Quality](#basics-code-quality)
- [Documentation](#basics-documentation)
- [File Names](#basics-file-names)
2. [React](#react)
3. [Redux](#redux)
Expand All @@ -29,6 +30,8 @@

<a name="basics-code-quality">**Code Quality:**</a> In general, you should write a neat and efficient code, covered by adequate amount of comments. Each JS module should have a brief header comment, outlining the content and purpose of that module. Comment all functions / methods, descibing what they do, how they work, what are the types and purposes of their arguments. You may omit such comments for standard React / Redux methods, small functions, etc. You should comment any non-trivial code; or trivial code that appears in strange places. If you note anything that can / should be improved in future, feel free to leave `TODO:` comments, and / or open issue tickets in the repo.

<a name="basics-documentation">**Documentation:**</a> All textual documentation must be in Markdown format, and it should be located inside [`/docs`](https://github.com/topcoder-platform/community-app/tree/develop/docs) or any of its sub-folders. To document REST APIs provided by our server we maintain a Postman collection in the same folder *Does not exist in the develop or master branches yet, but will appear in both soon*.

<a name="basics-file-names">**File Names:**</a> If a JSX file, or a folder containing `index.jsx`, exports a React component as the default export call it using CamelCase, i.g. `MyReactComponent.jsx`, or just `MyReactComponent` for folder. All other files an folders should be named as `yet-another-module.js`.

### <a name="react-redux">React</a>
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to-add-a-new-topcoder-community.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ To add a new community with the name **demo**, we should follow the following pr
- `points` - Points are shown rather than the prizes. The points are taken from `drPoints` field of challenge objects. There is no prizes tooltip in this case.
- `communityId` - *String* - Unique ID of this community.
- `communitySelector` - *Object Array* - Specifies data for the community selection dropdown inside the community header. Each object MUST HAVE `label` and `value` string fields, and MAY HAVE `redirect` field. If `redirect` field is specified, a click on that option in the dropdown will redirect user to the specified URL.
- `groupId` - *String* - This value of group ID is now used to fetch community statistics. Probably, it makes sense to use this value everywhere where `authorizedGroupIds` array is used, however, at the moment, these two are independent.
- `groupId` - *String* - Main user group of the community. All members of this group, and its descendant groups are treated as members of this community. Also, any challenges belonging to these groups are considered to be belonging to the community.
- `leaderboardApiUrl` - *String* - Endpoint from where the leaderboard data should be loaded.
- `logos` - *String Array | Object Array* - Array of image URLs to insert as logos into the left corner of community's header, alternatively the array may contain JS objects of shape
```
Expand Down
57 changes: 40 additions & 17 deletions src/server/tc-communities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
import _ from 'lodash';
import express from 'express';
import fs from 'fs';
import { getCommunitiesMetadata } from 'utils/tc';
import { getService as getGroupsService } from 'services/groups';
import {
addGroup,
getAuthTokens,
getCommunitiesMetadata,
isGroupMember,
} from 'utils/tc';

const router = express.Router();

Expand All @@ -16,30 +22,47 @@ const router = express.Router();
* should be included into the response.
*/
router.get('/', (req, res) => {
let apiGroups = {};
const tokens = getAuthTokens(req);
const groupsService = getGroupsService(tokens.tokenV3);
const list = [];
const groups = new Set(req.query.groups || []);
const communities = fs.readdirSync(__dirname);
communities.forEach((community) => {
const userGroups = req.query.groups
? req.query.groups.map(id => ({ id })) : [];
Promise.all(communities.map((community) => {
try {
const path = `${__dirname}/${community}/metadata.json`;
const data = JSON.parse(fs.readFileSync(path, 'utf8'));
if (!data.authorizedGroupIds
|| data.authorizedGroupIds.some(id => groups.has(id))) {
list.push({
challengeFilter: data.challengeFilter || {},
communityId: data.communityId,
communityName: data.communityName,
description: data.description,
groupId: data.groupId,
image: data.image,
});
}
const promise = data.authorizedGroupIds ? (
Promise.all(data.authorizedGroupIds.map((id) => {
if (!apiGroups[id]) {
return groupsService.get(id).then((group) => {
apiGroups = addGroup(apiGroups, group);
}).catch(_.noop);
}
return undefined;
}))
) : Promise.resolve();
return promise.then(() => {
if (!data.authorizedGroupIds
|| isGroupMember(data.authorizedGroupIds, userGroups, apiGroups)) {
list.push({
challengeFilter: data.challengeFilter || {},
communityId: data.communityId,
communityName: data.communityName,
description: data.description,
groupId: data.groupId,
image: data.image,
});
}
});
} catch (e) {
_.noop();
return undefined;
}
})).then(() => {
list.sort((a, b) => a.communityName.localeCompare(b.communityName));
res.json(list);
});
list.sort((a, b) => a.communityName.localeCompare(b.communityName));
res.json(list);
});

/**
Expand Down
2 changes: 1 addition & 1 deletion src/server/tc-communities/qa/metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"challengeFilter": {
"groupIds": ["20000004"]
"groupIds": ["20000004", "20000000"]
},
"communityId": "qa",
"communityName": "QaaS",
Expand Down
4 changes: 2 additions & 2 deletions src/server/tc-communities/wipro/metadata.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"authorizedGroupIds": [
"20000000", "20000004"
"20000000", "20000005", "200000011"
],
"challengeFilter": {
"groupIds": ["20000000"]
"groupIds": ["20000000", "20000004", "20000005", "20000007", "20000008", "20000009", "200000011"]
},
"challengeListing": {
"openChallengesInNewTabs": true
Expand Down
54 changes: 54 additions & 0 deletions src/shared/actions/groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Actions related to user groups.
*
* TODO: Some group-related actions can be found elsewhere (e.g. addition of
* members to group is located inside tc-communities actions, because joining
* a community is equivalent to adding user to a group). It will be great to
* move such actions in here.
*/

import { createActions } from 'redux-actions';
import { getService } from 'services/groups';

/* This pair of action creators allows to load detailed information about the
* specified user group. This information includes data on the sub-groups, thus
* related actions effective load entire tree of groups descendant from the
* specified one.
*
* Related segment of the Redux state is designed to keep information about
* many user groups at once. Thus, loading data about new groups does not drop
* previously loaded data on other groups (but, if previously loaded group is
* loaded again, related data are properly updated). Also, all requests to load
* user group data are handled in parallel, and new requests do not cancel
* previous ones, even if they have no been finished yet. */

/**
* Initiates loading of the user group data. Created action writes groupId into
* Redux state for book-keeping purposes. This action does not cancel previous
* unfinished requests to load group data.
* @param {String} groupId
* @return {String}
*/
function getInit(groupId) {
return groupId;
}

/**
* Actually loads user group data. Note that a proper v3 auth token is necessary
* to get user group data.
* @param {String} groupId
* @param {String} tokenV3
* @return {Object}
*/
function getDone(groupId, tokenV3) {
return getService(tokenV3).get(groupId)
.then(result => ({ groupId, result }))
.catch(error => ({ groupId, error }));
}

export default createActions({
GROUPS: {
GET_INIT: getInit,
GET_DONE: getDone,
},
});
5 changes: 3 additions & 2 deletions src/shared/actions/tc-communities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ function getList(auth) {
if (auth.profile && auth.profile.groups) {
groups = auth.profile.groups.map(g => g.id);
}
return fetch(`/api/tc-communities?${qs.stringify({ groups })}`)
.then(res => (res.ok ? res.json() : new Error(res.statusText)));
return fetch(`/api/tc-communities?${qs.stringify({ groups })}`, {
credentials: 'same-origin',
}).then(res => (res.ok ? res.json() : new Error(res.statusText)));
}

export default createActions({
Expand Down
51 changes: 36 additions & 15 deletions src/shared/components/Modal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,48 @@
* callback passed from the parent.
*/

/* global document */

import _ from 'lodash';
import React from 'react';
import PT from 'prop-types';
import _ from 'lodash';
import { themr } from 'react-css-super-themr';
import defaultStyle from './styles.scss';

function Modal(props) {
return (
<div>
<div
className={props.theme.container}
onWheel={event => event.preventDefault()}
>{props.children}</div>
<button
onClick={() => props.onCancel()}
onWheel={event => event.preventDefault()}
className={props.theme.overlay}
/>
</div>
);
/* NOTE: Modal component is implemented as class because we should append /
* remove a special class to the document's body to block its scrolling while
* keeping the modal's content scrollable. Unfortunately, just catching and
* manipulating on mouse wheel events does not help. */
class Modal extends React.Component {
componentDidMount() {
document.body.classList.add('scrolling-disabled-by-modal');
}

componentWillUnmount() {
document.body.classList.remove('scrolling-disabled-by-modal');
}

render() {
const {
children,
onCancel,
theme,
} = this.props;
return (
<div>
<div
className={theme.container}
onWheel={event => event.stopPropagation()}
>{children}</div>
<button
onClick={() => onCancel()}
className={theme.overlay}
/>
</div>
);
}
}

Modal.defaultProps = {
onCancel: _.noop,
children: null,
Expand Down
29 changes: 20 additions & 9 deletions src/shared/components/Modal/styles.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
@import '~styles/tc-styles';
$button-space-32: $base-unit * 6 + 2;
@import '~styles/mixins';

$button-space-32: 6 * $base-unit;

:global {
body.scrolling-disabled-by-modal {
overflow: hidden;
}
}

.overlay {
background: $tc-gray-neutral-dark;
Expand All @@ -11,25 +18,29 @@ $button-space-32: $base-unit * 6 + 2;
position: fixed;
top: 0;
width: 100%;
z-index: 99;
z-index: 998;
}

.container {
background: #fff;
box-shadow: 0 0 14px 1px rgba(38, 38, 40, 0.15);
border-radius: 4px;
padding: 40px;
width: 465px;
border-radius: 2 * $corner-radius;
max-height: 95vh;
max-width: $screen-md;
overflow: hidden;
padding: 8 * $base-unit;
width: 480px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;

@include xxs-to-md {
max-width: 95vw;
}

@include xxs-to-xs {
width: 92%;
left: 4%;
transform: translate(0, -50%);
padding: 40px 10px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function Submission(props) {
styleName={type === 'DESIGN' ? 'design-img' : 'dev-img'}
src={
submissionObject.preview ||
`${config.URL.STUDIO}?module=DownloadSubmission&sbmid=${submissionObject.submissionId}&sbt=tiny`
`${config.URL.STUDIO}?module=DownloadSubmission&sbmid=${submissionObject.submissionId}&sbt=tiny&sfi=1`
}
/>
</td>
Expand Down
Loading