Skip to content

Commit 3a5d6b2

Browse files
[Ready Release] Bug Bash Round 2 (#268)
1 parent fc5837a commit 3a5d6b2

File tree

19 files changed

+270
-59
lines changed

19 files changed

+270
-59
lines changed

src/actions/auth.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
*/
55

66
import { createActions } from "redux-actions";
7-
import { decodeToken } from "../utils/token";
7+
import _ from "lodash";
8+
import { decodeToken, readCookie } from "../utils/token";
89
import { getApiV3, getApiV5 } from "../services/challenge-api";
910
import { setErrorIcon, ERROR_ICON_TYPES } from "../utils/errors";
1011
import { getAuthUserTokens } from "@topcoder/micro-frontends-navbar-app";
12+
import { TOKEN_COOKIE_KEYS } from "../constants/index";
1113

1214
/**
1315
* Helper method that checks for HTTP error response v5 and throws Error in this case.
@@ -83,11 +85,27 @@ async function setAuthDone() {
8385
return user;
8486
}
8587

88+
/**
89+
* @static
90+
* @desc Check token cookies to find if a user is logged out:
91+
* This is because all the token cookies are cleared if a user is logged out.
92+
* @return {Action}
93+
*/
94+
function checkIsLoggedOut() {
95+
const tokenKeys = Object.keys(TOKEN_COOKIE_KEYS);
96+
const isLoggedOut = _.every(
97+
tokenKeys,
98+
(k) => readCookie(TOKEN_COOKIE_KEYS[k]) === undefined
99+
);
100+
return { isLoggedOut };
101+
}
102+
86103
export default createActions({
87104
AUTH: {
88105
LOAD_PROFILE: loadProfileDone,
89106
SET_TC_TOKEN_V2: setTcTokenV2,
90107
SET_TC_TOKEN_V3: setTcTokenV3,
91108
SET_AUTH_DONE: setAuthDone,
109+
CHECK_IS_LOGGED_OUT: checkIsLoggedOut,
92110
},
93111
});

src/actions/challenge.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,24 @@ function getChallengeDone(challengeId) {
454454
return challengeService.getChallenge(challengeId);
455455
}
456456

457+
/**
458+
* @static
459+
* @desc Check if a user has registered a challenge
460+
* @param {String} challengeId Challenge ID.
461+
* @param {String} userId User Id.
462+
* @return {Action}
463+
*/
464+
async function getIsRegistered(challengeId, userId) {
465+
const registrants = await challengeService.getChallengeRegistrants(
466+
challengeId
467+
);
468+
const isRegistered = _.some(
469+
registrants,
470+
(r) => `${r.memberId}` === `${userId}`
471+
);
472+
return { isRegistered };
473+
}
474+
457475
export default createActions({
458476
CHALLENGE: {
459477
DROP_CHECKPOINTS: dropCheckpoints,
@@ -483,5 +501,6 @@ export default createActions({
483501
GET_SUBMISSION_INFORMATION_DONE: getSubmissionInformationDone,
484502
GET_CHALLENGE_INIT: _.noop,
485503
GET_CHALLENGE_DONE: getChallengeDone,
504+
GET_IS_REGISTERED: getIsRegistered,
486505
},
487506
});

src/components/DateRangePicker/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ function DateRangePicker(props) {
298298
});
299299

300300
setIsComponentVisible(false);
301-
}
301+
};
302302

303303
/**
304304
* Event handler on date selection changes

src/components/challenge-detail/Header/ChallengeTags.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ export default function ChallengeTags(props) {
127127
onClick={() =>
128128
setImmediate(() => setChallengeListingFilter({ tags: [tag] }))
129129
}
130-
to={`${challengesUrl}${filterByTag}&tags[]=${encodeURIComponent(tag)}`}
130+
to={`${challengesUrl}${filterByTag}&tags[]=${encodeURIComponent(
131+
tag
132+
)}`}
131133
>
132134
{tag}
133135
</Tag>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from "react";
2+
import "./styles.module.scss";
3+
4+
export default function ChallengeLoading() {
5+
return (
6+
<div styleName="challenge-loading">
7+
<div styleName="track placeholder-template"></div>
8+
<div styleName="main">
9+
<div styleName="title placeholder-template"></div>
10+
<div styleName="info placeholder-template"></div>
11+
<div styleName="footer placeholder-template"></div>
12+
</div>
13+
<div>
14+
<div styleName="prize placeholder-template"></div>
15+
<div styleName="prize-nominal placeholder-template"></div>
16+
</div>
17+
</div>
18+
);
19+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
@import "~styles/mixins";
2+
3+
@keyframes placeholderAnim {
4+
0% {
5+
background-position: -$base-unit * 94 0;
6+
}
7+
8+
100% {
9+
background-position: $base-unit * 94 0;
10+
}
11+
}
12+
13+
.animated-placeholder-template {
14+
animation-duration: 1.25s;
15+
animation-fill-mode: forwards;
16+
animation-iteration-count: infinite;
17+
animation-name: placeholderAnim;
18+
animation-timing-function: linear;
19+
background: $tc-gray-neutral-dark;
20+
background: linear-gradient(to right, $tc-gray-neutral-dark 8%, $tc-gray-10 18%, $tc-gray-neutral-dark 33%);
21+
background-size: $base-unit * 256 $base-unit * 20;
22+
position: relative;
23+
}
24+
25+
.placeholder-template {
26+
border-radius: $corner-radius;
27+
28+
@extend .animated-placeholder-template;
29+
}
30+
31+
.challenge-loading {
32+
height: 126px;
33+
padding: 16px;
34+
margin: 0 24px;
35+
display: flex;
36+
border-bottom: 1px solid #E9E9E9;
37+
border-top: 1px solid #E9E9E9;
38+
39+
> div {
40+
margin-right: 16px;
41+
}
42+
43+
&:nth-child(even) {
44+
background: #FBFBFB;
45+
}
46+
.track {
47+
flex: 1 0 46px;
48+
width: 46px;
49+
height: 44px;
50+
}
51+
52+
.main {
53+
flex: 1 1 70%;
54+
}
55+
56+
.title {
57+
height: 22px;
58+
width: 100px;
59+
margin-bottom: 8px;
60+
}
61+
62+
.info {
63+
height: 18px;
64+
width: 200px;
65+
margin-bottom: 16px;
66+
}
67+
68+
.footer {
69+
height: 18px;
70+
width: 70%;
71+
}
72+
73+
.prize {
74+
height: 18px;
75+
width: 60px;
76+
margin-bottom: 8px;
77+
margin-right: 40px;
78+
}
79+
80+
.prize-nominal {
81+
height: 42px;
82+
width: 40px;
83+
}
84+
}

src/constants/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,9 @@ export const COMPETITION_TRACKS = {
9696
DEV: "Development",
9797
QA: "Quality Assurance",
9898
};
99+
100+
export const TOKEN_COOKIE_KEYS = {
101+
V3JWT: "v3jwt",
102+
TCJWT: "tcjwt",
103+
TCSSO: "tcsso",
104+
};

src/containers/Challenges/Listing/ChallengeItem/index.jsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import * as utils from "../../../../utils";
1212
import ProgressTooltip from "../tooltips/ProgressTooltip";
1313
import PlacementsTooltip from "../tooltips/PlacementsTooltip";
1414
import TagsMoreTooltip from "../tooltips/TagsMoreTooltip";
15-
import { CHALLENGES_URL } from 'constants';
16-
import { Link } from '@reach/router';
15+
import { CHALLENGES_URL } from "constants";
16+
import { Link } from "@reach/router";
1717

1818
import "./styles.scss";
1919

@@ -45,9 +45,7 @@ const ChallengeItem = ({ challenge, onClickTag, onClickTrack, isLoggedIn }) => {
4545
<div styleName="info">
4646
<div styleName="name-container">
4747
<h6 styleName="name">
48-
<Link
49-
to={`${CHALLENGES_URL}/${challenge.id}`}
50-
>
48+
<Link to={`${CHALLENGES_URL}/${challenge.id}`}>
5149
{challenge.name}
5250
</Link>
5351
</h6>
@@ -72,9 +70,7 @@ const ChallengeItem = ({ challenge, onClickTag, onClickTrack, isLoggedIn }) => {
7270
/>
7371
</div>
7472
<div styleName="nums">
75-
<Link
76-
to={`${CHALLENGES_URL}/${challenge.id}?tab=registrants`}
77-
>
73+
<Link to={`${CHALLENGES_URL}/${challenge.id}?tab=registrants`}>
7874
<NumRegistrants numOfRegistrants={challenge.numOfRegistrants} />
7975
</Link>
8076
<Link to={submissionLink}>

src/containers/Challenges/Listing/index.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import ChallengeItem from "./ChallengeItem";
99
import TextInput from "../../../components/TextInput";
1010
import Dropdown from "../../../components/Dropdown";
1111
import DateRangePicker from "../../../components/DateRangePicker";
12+
import ChallengeLoading from "../../../components/challenge-listing/ChallengeLoading";
1213
import * as utils from "../../../utils";
14+
1315
import * as constants from "../../../constants";
1416
import IconSearch from "../../../assets/icons/search.svg";
15-
1617
import "./styles.scss";
1718

1819
const Listing = ({
1920
challenges,
21+
loadingChallenges,
2022
search,
2123
page,
2224
perPage,
@@ -111,7 +113,9 @@ const Listing = ({
111113
</div>
112114
</div>
113115
</Panel.Header>
114-
{challenges.length ? (
116+
{loadingChallenges ? (
117+
_.times(3, () => <ChallengeLoading />)
118+
) : challenges.length ? (
115119
<Panel.Body>
116120
{challenges.map((challenge, index) => (
117121
<div
@@ -162,6 +166,7 @@ const Listing = ({
162166

163167
Listing.propTypes = {
164168
challenges: PT.arrayOf(PT.shape()),
169+
loadingChallenges: PT.bool,
165170
search: PT.string,
166171
page: PT.number,
167172
perPage: PT.number,

src/containers/Challenges/index.jsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import { connect } from "react-redux";
44
import Listing from "./Listing";
55
import actions from "../../actions";
66
// import ChallengeRecommendedError from "./Listing/errors/ChallengeRecommendedError";
7+
import LoadingIndicator from "../../components/LoadingIndicator";
8+
import Panel from "../../components/Panel";
79
import * as constants from "../../constants";
8-
import IconListView from "../../assets/icons/list-view.svg";
9-
import IconCardView from "../../assets/icons/card-view.svg";
10+
// import IconListView from "../../assets/icons/list-view.svg";
11+
// import IconCardView from "../../assets/icons/card-view.svg";
1012
import { Banner } from "@topcoder/micro-frontends-earn-app";
11-
import * as utils from "../../utils";
1213

14+
import * as utils from "../../utils";
1315
import "./styles.scss";
1416

1517
const Challenges = ({
@@ -29,6 +31,7 @@ const Challenges = ({
2931
initialized,
3032
updateQuery,
3133
tags,
34+
loadingChallenges,
3235
}) => {
3336
const [isLoggedIn, setIsLoggedIn] = useState(null);
3437

@@ -76,20 +79,21 @@ const Challenges = ({
7679
<Banner />
7780
<h1 styleName="title">
7881
<span>CHALLENGES</span>
79-
<span styleName="view-mode">
82+
{/* <span styleName="view-mode">
8083
<button styleName="button-icon active">
8184
<IconListView />
8285
</button>
8386
<button styleName="button-icon">
8487
<IconCardView />
8588
</button>
86-
</span>
89+
</span> */}
8790
</h1>
88-
{initialized && (
91+
{initialized ? (
8992
<>
9093
{/*noRecommendedChallenges && <ChallengeRecommendedError />*/}
9194
<Listing
9295
challenges={challenges}
96+
loadingChallenges={loadingChallenges}
9397
search={search}
9498
page={page}
9599
perPage={perPage}
@@ -107,6 +111,12 @@ const Challenges = ({
107111
isLoggedIn={isLoggedIn}
108112
/>
109113
</>
114+
) : (
115+
<Panel>
116+
<Panel.Body>
117+
<LoadingIndicator />
118+
</Panel.Body>
119+
</Panel>
110120
)}
111121
</div>
112122
);
@@ -128,6 +138,7 @@ Challenges.propTypes = {
128138
initialized: PT.bool,
129139
updateQuery: PT.func,
130140
tags: PT.arrayOf(PT.string),
141+
loadingChallenges: PT.bool,
131142
};
132143

133144
const mapStateToProps = (state) => ({
@@ -146,6 +157,7 @@ const mapStateToProps = (state) => ({
146157
recommendedChallenges: state.challenges.recommendedChallenges,
147158
initialized: state.challenges.initialized,
148159
tags: state.filter.challenge.tags,
160+
loadingChallenges: state.challenges.loadingChallenges,
149161
});
150162

151163
const mapDispatchToProps = {

src/containers/Filter/ChallengeFilter/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const ChallengeFilter = ({
121121
updateFilter(filterChange);
122122
}}
123123
/>
124-
<span>{track.replace('Quality Assurance', 'QA')}</span>
124+
<span>{track.replace("Quality Assurance", "QA")}</span>
125125
</span>
126126
))}
127127
</div>

0 commit comments

Comments
 (0)