Skip to content

Commit a861093

Browse files
committed
Improvement of Join Community button
Enhances join community flow for non-authenticated (and potentially not-registered) visitors.
1 parent 3aa351d commit a861093

File tree

7 files changed

+225
-95
lines changed

7 files changed

+225
-95
lines changed

__tests__/shared/components/tc-communities/__snapshots__/JoinCommunity.jsx.snap

Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,14 @@ exports[`Matches shallow shapshot 1`] = `
88
>
99
Join Community
1010
</button>
11-
<ThemedModal
12-
composeTheme="deeply"
13-
mapThemrProps={[Function]}
14-
onCancel={[Function]}
15-
theme={Object {}}
16-
>
17-
<p
18-
className="src-shared-components-tc-communities-JoinCommunity-___style__confirmMsg___3Urx0"
19-
>
20-
Are you sure you want to join
21-
ios
22-
?
23-
</p>
24-
<div
25-
className="src-shared-components-tc-communities-JoinCommunity-___style__buttons___24jiR"
26-
>
27-
<button
28-
className="src-shared-components-tc-communities-JoinCommunity-___style__btnConfirm___21w2M"
29-
onClick={[Function]}
30-
>
31-
Join
32-
</button>
33-
<button
34-
className="src-shared-components-tc-communities-JoinCommunity-___style__btnCancel___21ib7"
35-
onClick={[Function]}
36-
>
37-
Cancel
38-
</button>
39-
</div>
40-
</ThemedModal>
11+
<ConfirmModal
12+
communityName="ios"
13+
groupId="1"
14+
join={[Function]}
15+
resetJoinButton={[Function]}
16+
token="token"
17+
userId="useId"
18+
/>
4119
</div>
4220
`;
4321

@@ -115,35 +93,13 @@ exports[`Matches shallow shapshot 5`] = `
11593
>
11694
Join Community
11795
</button>
118-
<ThemedModal
119-
composeTheme="deeply"
120-
mapThemrProps={[Function]}
121-
onCancel={[Function]}
122-
theme={Object {}}
123-
>
124-
<p
125-
className="src-shared-components-tc-communities-JoinCommunity-___style__confirmMsg___3Urx0"
126-
>
127-
Are you sure you want to join
128-
ios
129-
?
130-
</p>
131-
<div
132-
className="src-shared-components-tc-communities-JoinCommunity-___style__buttons___24jiR"
133-
>
134-
<button
135-
className="src-shared-components-tc-communities-JoinCommunity-___style__btnConfirm___21w2M"
136-
onClick={[Function]}
137-
>
138-
Join
139-
</button>
140-
<button
141-
className="src-shared-components-tc-communities-JoinCommunity-___style__btnCancel___21ib7"
142-
onClick={[Function]}
143-
>
144-
Cancel
145-
</button>
146-
</div>
147-
</ThemedModal>
96+
<ConfirmModal
97+
communityName="ios"
98+
groupId="1"
99+
join={[Function]}
100+
resetJoinButton={[Function]}
101+
token={null}
102+
userId="useId"
103+
/>
148104
</div>
149105
`;

src/server/tc-communities/blockchain/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"groupIds": ["20000010"]
44
},
55
"communityId": "blockchain",
6-
"communityName": "Blockchain",
6+
"communityName": "Blockchain Community",
77
"communitySelector": [{
88
"label": "Blockchain Community",
99
"value": "1"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* The modal asking visitor to confirm that he wants to join community where
3+
* JoinCommunity component is rendered. If visitor is authenticated it shows
4+
* Join / Cancel buttons that do what they are supposed to do; otherwise it
5+
* shows Register & Join / Log In & Join / Cancel buttons, first of which get
6+
* visitor to registration / login flows, with return url making the join to
7+
* happen automatically.
8+
*/
9+
10+
/* global window */
11+
12+
import config from 'utils/config';
13+
import Modal from 'components/Modal';
14+
import PT from 'prop-types';
15+
import React from 'react';
16+
import './style.scss';
17+
18+
export default function ConfirmModal({
19+
communityName,
20+
groupId,
21+
join,
22+
resetJoinButton,
23+
token,
24+
userId,
25+
}) {
26+
return (
27+
<Modal onCancel={resetJoinButton}>
28+
<div styleName="confirmMsg">
29+
{ userId ? null : (
30+
<p>To join you must login / register into Topcoder platform!</p>
31+
)}
32+
Are you sure you want to join {communityName}?
33+
</div>
34+
<div styleName="buttons">
35+
{ userId ? (
36+
<span>
37+
<button
38+
onClick={() => join(token, groupId, userId)}
39+
styleName="btnConfirm"
40+
>Join</button>
41+
<button
42+
onClick={resetJoinButton}
43+
styleName="btnCancel"
44+
>Cancel</button>
45+
</span>
46+
) : (
47+
<span>
48+
<button
49+
onClick={() => {
50+
const url = `${
51+
encodeURIComponent(window.location.href)
52+
}?join=${groupId}`;
53+
window.location = `${config.URL.AUTH}/member?retUrl=${url}`;
54+
}}
55+
styleName="btnConfirmLong"
56+
>Log In & Join</button>
57+
<button
58+
onClick={() => {
59+
const url = `${
60+
encodeURIComponent(window.location.href)
61+
}?join=${groupId}`;
62+
window.location = `${config.URL.AUTH}/member/registration?retUrl=${url}`;
63+
}}
64+
styleName="btnConfirmLong"
65+
>Register & Join</button>
66+
<button
67+
onClick={resetJoinButton}
68+
styleName="btnCancelLong"
69+
>Cancel</button>
70+
</span>
71+
)}
72+
</div>
73+
</Modal>
74+
);
75+
}
76+
77+
ConfirmModal.defaultProps = {
78+
token: null,
79+
userId: null,
80+
};
81+
82+
ConfirmModal.propTypes = {
83+
communityName: PT.string.isRequired,
84+
groupId: PT.string.isRequired,
85+
join: PT.func.isRequired,
86+
resetJoinButton: PT.func.isRequired,
87+
token: PT.string,
88+
userId: PT.string,
89+
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
@import "~styles/mixins";
2+
3+
/* TODO: Should be updated to use standard themable buttons! */
4+
5+
@mixin button($color, $highlight-color) {
6+
color: $color;
7+
display: block;
8+
background: $tc-white;
9+
border: 1px solid $color;
10+
border-radius: 20px;
11+
font: 700 14px/40px 'Open Sans';
12+
margin: 24px auto;
13+
height: 40px;
14+
text-align: center;
15+
text-transform: uppercase;
16+
width: 171px;
17+
18+
&:hover,
19+
&:active,
20+
&:focus,
21+
&:visited {
22+
color: $color;
23+
outline: none;
24+
text-decoration: none;
25+
}
26+
27+
&:hover {
28+
background: $highlight-color;
29+
}
30+
}
31+
32+
.confirmMsg {
33+
@include tc-body;
34+
35+
color: #787d81;
36+
text-align: center;
37+
}
38+
39+
.btnCancel {
40+
@include button($tc-light-blue, $tc-light-blue-10);
41+
42+
display: inline-block;
43+
margin: 24px 12px 0;
44+
width: 100px;
45+
}
46+
47+
.btnCancelLong {
48+
@include button($tc-light-blue, $tc-light-blue-10);
49+
50+
display: inline-block;
51+
margin: 24px 12px 0;
52+
width: 200px;
53+
}
54+
55+
.btnConfirm {
56+
@include button($tc-green, $tc-green-10);
57+
58+
display: inline-block;
59+
margin: 24px 12px 0;
60+
width: 100px;
61+
}
62+
63+
.btnConfirmLong {
64+
@include button($tc-green, $tc-green-10);
65+
66+
display: inline-block;
67+
margin: 24px 12px 0;
68+
width: 200px;
69+
}
70+
71+
.buttons {
72+
text-align: center;
73+
}

src/shared/components/tc-communities/JoinCommunity/index.jsx

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
* modal is shown on success.
66
*/
77

8-
/* global window */
9-
108
import _ from 'lodash';
11-
import config from 'utils/config';
129
import LoadingIndicator from 'components/LoadingIndicator';
1310
import Modal from 'components/Modal';
1411
import PT from 'prop-types';
1512
import React from 'react';
1613
import style from './style.scss';
1714

15+
import ConfirmModal from './ConfirmModal';
16+
1817
export const STATE = {
1918
CONFIRM_JOIN: 'confirm-join',
2019
DEFAULT: 'default',
@@ -45,13 +44,7 @@ export default function JoinCommunity({
4544
return;
4645
default:
4746
}
48-
if (token) showJoinConfirmModal();
49-
else {
50-
/* If our visitor is not authenticated, the button redirects to
51-
* login page, with return URL set back to this page. */
52-
const url = encodeURIComponent(window.location.href);
53-
window.location = `${config.URL.AUTH}/member?retUrl=${url}`;
54-
}
47+
showJoinConfirmModal();
5548
}}
5649
styleName={`link ${state === STATE.JOINING ? 'joining' : ''}`}
5750
>
@@ -73,21 +66,14 @@ export default function JoinCommunity({
7366
</Modal>
7467
) : null}
7568
{ state === STATE.CONFIRM_JOIN ? (
76-
<Modal onCancel={resetJoinButton}>
77-
<p styleName="confirmMsg">
78-
Are you sure you want to join {communityName}?
79-
</p>
80-
<div styleName="buttons">
81-
<button
82-
onClick={() => join(token, groupId, userId)}
83-
styleName="btnConfirm"
84-
>Join</button>
85-
<button
86-
onClick={resetJoinButton}
87-
styleName="btnCancel"
88-
>Cancel</button>
89-
</div>
90-
</Modal>
69+
<ConfirmModal
70+
communityName={communityName}
71+
groupId={groupId}
72+
join={join}
73+
resetJoinButton={resetJoinButton}
74+
token={token}
75+
userId={userId}
76+
/>
9177
) : null}
9278
</div>
9379
);
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
@import "~styles/mixins";
22

33
.message {
4-
color: #394146 !important;
4+
color: #787d81 !important;
55
font: 15px/160% 'Open Sans' !important;
66
justify-content: center !important;
77
margin-top: 19px !important;
8-
opacity: 0.7 !important;
98
}

src/shared/reducers/tc-communities/index.js

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
import _ from 'lodash';
88
import actions from 'actions/tc-communities';
9+
import config from 'utils/config';
910
import logger from 'utils/logger';
1011
import { handleActions } from 'redux-actions';
11-
import { combine, resolveReducers } from 'utils/redux';
12+
import { decodeToken, isTokenExpired } from 'tc-accounts';
13+
import { combine, resolveReducers, toFSA } from 'utils/redux';
1214
import { STATE as JOIN_COMMUNITY } from 'components/tc-communities/JoinCommunity';
1315

1416
import { factory as metaFactory } from './meta';
@@ -55,12 +57,37 @@ function create(initialState = {}) {
5557
}
5658

5759
export function factory(req) {
58-
return resolveReducers({
59-
meta: metaFactory(req),
60-
news: newsFactory(req),
61-
}).then(reducers => combine(create(), {
62-
...reducers,
63-
}));
60+
let joinPromise;
61+
if (req) {
62+
const cookies = req.cookies || {};
63+
const adt = config.AUTH_DROP_TIME;
64+
let tokenV3 = cookies.v3jwt;
65+
if (!tokenV3 || isTokenExpired(tokenV3, adt)) tokenV3 = null;
66+
67+
const joinGroupId = req.query && req.query.join;
68+
if (joinGroupId && tokenV3) {
69+
const user = decodeToken(tokenV3);
70+
joinPromise = toFSA(
71+
actions.tcCommunity.joinDone(tokenV3, joinGroupId, user.userId),
72+
);
73+
}
74+
}
75+
76+
return Promise.all([
77+
resolveReducers({
78+
meta: metaFactory(req),
79+
news: newsFactory(req),
80+
}),
81+
joinPromise,
82+
]).then(([reducers, joinResult]) => {
83+
let state;
84+
if (joinResult) {
85+
state = onJoinDone({}, joinResult);
86+
}
87+
return combine(create(state), {
88+
...reducers,
89+
});
90+
});
6491
}
6592

6693
export default undefined;

0 commit comments

Comments
 (0)