Skip to content

Commit 22b2073

Browse files
Merge pull request #6727 from topcoder-platform/feature-timeline-wall
Merging Timeline Wall fixes to Develop branch
2 parents 11e582a + ac52559 commit 22b2073

File tree

15 files changed

+178
-39
lines changed

15 files changed

+178
-39
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ exports[`Matches shallow shapshot 2`] = `
6565
Joining...
6666
</span>
6767
<ThemedLoadingIndicator
68+
className=""
6869
composeAdhocTheme="deeply"
6970
composeContextTheme="softly"
7071
mapThemrProps={[Function]}

config/default.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ module.exports = {
167167
SUBDOMAIN_PROFILE_CONFIG: [{
168168
groupId: '20000000', communityId: 'wipro', communityName: 'topgear', userProfile: 'https://topgear-app.wipro.com/user-details',
169169
}],
170-
TIMELNE_EVENT_API: 'https://api.topcoder-dev.com/v5/timeline-wall',
170+
TIMELINE_WALL_API: 'https://api.topcoder-dev.com/v5/timeline-wall',
171171
},
172172

173173
/* Information about Topcoder user groups can be cached in various places.
@@ -465,7 +465,8 @@ module.exports = {
465465
PLATFORMUI_SITE_URL: 'https://platform-ui.topcoder-dev.com',
466466
DICE_VERIFY_URL: 'https://accounts-auth0.topcoder-dev.com',
467467
TIMELINE: {
468-
REJECTION_EVENT_REASONS: ['Duplicate Event'],
468+
REJECTION_EVENT_REASONS: ['Duplicate Event', 'Violates the Topcoder terms', 'Inaccurate or Invalid'],
469469
ALLOWED_FILETYPES: ['image/jpeg', 'image/png', 'video/mp4', 'video/x-msvideo', 'video/webm'],
470+
FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL: 5 * 60 * 1000, // 5 minutes
470471
},
471472
};

config/production.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ module.exports = {
6262
},
6363
EMAIL_VERIFY_URL: 'http://www.topcoder.com/settings/account/changeEmail',
6464
THRIVE_FEED: 'https://topcoder.com/api/feeds/thrive',
65-
TIMELNE_EVENT_API: 'https://api.topcoder.com/v5/timeline-wall',
65+
TIMELINE_WALL_API: 'https://api.topcoder.com/v5/timeline-wall',
6666
},
6767
/* Filestack configuration for uploading Submissions
6868
* These are for the production back end */
Lines changed: 3 additions & 0 deletions
Loading

src/shared/components/LoadingIndicator/index.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import PT from 'prop-types';
99
import React from 'react';
1010
import { themr } from 'react-css-super-themr';
11+
import cn from 'classnames';
1112
import style from './styles.scss';
1213

13-
const LoadingIndicator = ({ theme }) => (
14+
const LoadingIndicator = ({ theme, className }) => (
1415
<svg
15-
className={theme.container}
16+
className={cn(theme.container, className)}
1617
viewBox="0 0 64 64"
1718
>
1819
<circle
@@ -32,8 +33,13 @@ const LoadingIndicator = ({ theme }) => (
3233
</svg>
3334
);
3435

36+
LoadingIndicator.defaultProps = {
37+
className: '',
38+
};
39+
3540
LoadingIndicator.propTypes = {
3641
theme: PT.shape().isRequired,
42+
className: PT.string,
3743
};
3844

3945
export default themr('LoadingIndicator', style)(LoadingIndicator);

src/shared/containers/timeline-wall/index.jsx

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, {
2+
useState, useEffect, useRef, useMemo,
3+
} from 'react';
24
import PT from 'prop-types';
35
import { connect } from 'react-redux';
46
import TopBanner from 'assets/images/timeline-wall/top-banner.png';
57
import TopBannerMobile from 'assets/images/timeline-wall/top-banner-mobile.png';
68
import IconCheveronDownBlue from 'assets/images/timeline-wall/cheveron-down-blue.svg';
9+
import IconArrowRight from 'assets/images/timeline-wall/icon-arrow-right.svg';
710
import { deleteEventById, approveEventById, rejectEventById } from 'services/timelineWall';
811
import cn from 'classnames';
912
import moment from 'moment';
1013
import { useMediaQuery } from 'react-responsive';
1114
import _ from 'lodash';
15+
import { config } from 'topcoder-react-utils';
1216
import timelineActions from 'actions/timelineWall';
1317
import LoadingIndicator from 'components/LoadingIndicator';
1418
import TimelineEvents from './timeline-events';
1519
import PendingApprovals from './pending-approvals';
1620

1721
import './styles.scss';
1822

23+
24+
const FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL = _.get(config, 'TIMELINE.FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL', 0);
1925
function TimelineWallContainer(props) {
2026
const [tab, setTab] = useState(0);
27+
const fetchingApprovalsInterval = useRef(null);
2128
const [showRightFilterMobile, setShowRightFilterMobile] = useState(false);
2229
const [selectedFilterValue, setSelectedFilterValue] = useState({
2330
year: 0,
@@ -36,13 +43,15 @@ function TimelineWallContainer(props) {
3643
getAvatar,
3744
userAvatars,
3845
pendingApprovals,
46+
loadingApprovals,
3947
uploading,
48+
uploadResult,
4049
} = props;
4150

4251
const role = 'Admin User';
4352
const authToken = _.get(auth, 'tokenV3');
4453
const isMobile = useMediaQuery({
45-
query: '(max-device-width: 768px)',
54+
query: '(max-width: 768px)',
4655
});
4756

4857
useEffect(() => {
@@ -54,9 +63,25 @@ function TimelineWallContainer(props) {
5463
}, []);
5564

5665
useEffect(() => {
57-
if (authToken && isAdmin && !pendingApprovals.length) {
66+
if (fetchingApprovalsInterval.current) {
67+
clearInterval(fetchingApprovalsInterval.current);
68+
fetchingApprovalsInterval.current = null;
69+
}
70+
if (authToken && isAdmin) {
5871
getPendingApprovals(authToken);
72+
if (FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL) {
73+
fetchingApprovalsInterval.current = setInterval(() => {
74+
getPendingApprovals(authToken);
75+
}, FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL);
76+
}
5977
}
78+
79+
return () => {
80+
if (fetchingApprovalsInterval.current) {
81+
clearInterval(fetchingApprovalsInterval.current);
82+
fetchingApprovalsInterval.current = null;
83+
}
84+
};
6085
}, [isAdmin]);
6186

6287
useEffect(() => {
@@ -71,7 +96,7 @@ function TimelineWallContainer(props) {
7196
}, [events]);
7297

7398
useEffect(() => {
74-
if ((pendingApprovals || []).length) {
99+
if (pendingApprovals.length) {
75100
_.uniqBy(pendingApprovals, 'createdBy').forEach((eventItem) => {
76101
const photoURL = _.get(userAvatars, eventItem.createdBy);
77102
if (!photoURL) {
@@ -89,7 +114,7 @@ function TimelineWallContainer(props) {
89114
let date = moment(`${currentYear}-${currentMonth + 1}`).format('YYYY-MM');
90115

91116
while (!target) {
92-
target = document.getElementById(`${moment(date).year()}-${(moment(date).month()).toString().padStart(2, '0')}`);
117+
target = document.getElementById(`${isMobile ? 'mobile-' : 'desktop-'}${moment(date).year()}-${(moment(date).month()).toString().padStart(2, '0')}`);
93118

94119
if (target || !moment(date).isValid() || moment(date).year() > maxYear) {
95120
break;
@@ -100,11 +125,7 @@ function TimelineWallContainer(props) {
100125
if (target) {
101126
const yOffset = -10;
102127
const coordinate = target.getBoundingClientRect().top + window.pageYOffset + yOffset;
103-
if (isMobile) {
104-
setTimeout(target.scrollTo(), 100);
105-
} else {
106-
window.scrollTo({ top: coordinate, behavior: 'smooth' });
107-
}
128+
window.scrollTo({ top: coordinate, behavior: 'smooth' });
108129
} else {
109130
window.scrollTo({ top: 0, behavior: 'smooth' });
110131
}
@@ -137,10 +158,23 @@ function TimelineWallContainer(props) {
137158
};
138159

139160
const sortedEvents = _.orderBy(events, ['eventDate'], ['desc']);
161+
const shouldShowDiscuss = useMemo(() => {
162+
if (tab !== 0) {
163+
return false;
164+
}
165+
if (isAdmin) {
166+
return !isMobile;
167+
}
168+
return true;
169+
}, [isAdmin, isMobile, tab]);
140170

141171
return (
142172
<div styleName="container">
143-
<div styleName={isAdmin ? 'header header-admin' : 'header'}>
173+
<div styleName={cn('header', {
174+
'header-admin': isAdmin,
175+
'header-with-discuss': shouldShowDiscuss,
176+
})}
177+
>
144178
<img src={TopBanner} alt="top-banner" styleName="header-bg hide-mobile" />
145179
<img src={TopBannerMobile} alt="top-banner" styleName="header-bg hide-desktop show-mobile" />
146180

@@ -170,6 +204,16 @@ function TimelineWallContainer(props) {
170204
</div>
171205
) : (<h1 styleName="header-content-1">Topcoder Timeline Wall</h1>)}
172206

207+
{shouldShowDiscuss ? (
208+
<button
209+
type="button"
210+
styleName="btn-discuss"
211+
>
212+
<span>DISCUSS</span>
213+
<IconArrowRight />
214+
</button>
215+
) : null}
216+
173217
<button
174218
onClick={() => {
175219
setShowRightFilterMobile(true);
@@ -203,12 +247,15 @@ function TimelineWallContainer(props) {
203247
getAvatar={getAvatar}
204248
userAvatars={userAvatars}
205249
uploading={uploading}
250+
uploadResult={uploadResult}
206251
deleteEvent={deleteEvent}
207252
/>
208253
<React.Fragment>
209254
{
210-
loading ? (
211-
<LoadingIndicator />
255+
loadingApprovals ? (
256+
<LoadingIndicator
257+
styleName={cn({ hide: tab === 0 })}
258+
/>
212259
) : (
213260
<PendingApprovals
214261
events={pendingApprovals}
@@ -234,7 +281,9 @@ TimelineWallContainer.defaultProps = {
234281
auth: null,
235282
isAdmin: false,
236283
loading: false,
284+
loadingApprovals: false,
237285
uploading: false,
286+
uploadResult: '',
238287
events: [],
239288
userAvatars: {},
240289
pendingApprovals: [],
@@ -247,7 +296,9 @@ TimelineWallContainer.propTypes = {
247296
auth: PT.shape(),
248297
isAdmin: PT.bool,
249298
loading: PT.bool,
299+
loadingApprovals: PT.bool,
250300
uploading: PT.bool,
301+
uploadResult: PT.string,
251302
events: PT.arrayOf(PT.shape()),
252303
loadUserDetails: PT.func.isRequired,
253304
createNewEvent: PT.func.isRequired,
@@ -264,10 +315,13 @@ const mapStateToProps = state => ({
264315
},
265316
isAdmin: state.timelineWall.isAdmin,
266317
loading: state.timelineWall.loading,
318+
loadingApprovals: state.timelineWall.loadingApprovals
319+
&& !(state.timelineWall.pendingApprovals || []).length,
267320
uploading: state.timelineWall.uploading,
321+
uploadResult: state.timelineWall.uploadResult,
268322
events: state.timelineWall.events,
269323
userAvatars: state.timelineWall.userAvatars,
270-
pendingApprovals: state.timelineWall.pendingApprovals,
324+
pendingApprovals: state.timelineWall.pendingApprovals || [],
271325
});
272326

273327
function mapDispatchToProps(dispatch) {

src/shared/containers/timeline-wall/modal-event-add/index.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import LoadingIndicator from 'components/LoadingIndicator';
66

77
import style from './styles.scss';
88

9-
function ModalEventAdd({ onClose, isAdmin, uploading }) {
9+
function ModalEventAdd({
10+
onClose, isAdmin, uploading, uploadResult,
11+
}) {
12+
const successMessage = !isAdmin ? 'Thank you! Your event was submitted for review. You’ll receive an email once the review is completed'
13+
: 'Thank you! Your event was added to the Timeline Wall.';
1014
return (
1115
<Modal
1216
theme={{ container: style.container, overlay: style.overlay }}
@@ -22,8 +26,7 @@ function ModalEventAdd({ onClose, isAdmin, uploading }) {
2226
) : (
2327
<span styleName="text-description">
2428
{
25-
!isAdmin ? 'Thank you! Your event was submitted for review. You’ll receive an email once the review is completed'
26-
: 'Thank you! Your event was added to the Timeline Wall.'
29+
uploadResult || successMessage
2730
}
2831
</span>
2932
)
@@ -56,6 +59,7 @@ ModalEventAdd.defaultProps = {
5659
onClose: () => { },
5760
isAdmin: false,
5861
uploading: false,
62+
uploadResult: '',
5963
};
6064

6165
/**
@@ -65,6 +69,7 @@ ModalEventAdd.propTypes = {
6569
onClose: PT.func,
6670
isAdmin: PT.bool,
6771
uploading: PT.bool,
72+
uploadResult: PT.string,
6873
};
6974

7075
export default ModalEventAdd;

src/shared/containers/timeline-wall/pending-approvals/approval-item/index.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ function ApprovalItem({
3535
<span styleName="text-date">{moment(event.eventDate).format('MMM DD, YYYY')}</span>
3636
<div styleName="separator" />
3737
<img width="24" height="24" src={photoURL} alt="avatar" />
38-
<span styleName="text-handle">{event.createdBy}</span>
38+
<a
39+
href={`${window.origin}/members/${event.createdBy}`}
40+
target={`${_.includes(window.origin, 'www') ? '_self' : '_blank'}`}
41+
rel="noopener noreferrer"
42+
styleName="text-handle"
43+
>{event.createdBy}
44+
</a>
3945
</div>
4046
<span styleName="text-date">Submitted date: {moment(event.submitedDate).format('MMM DD, YYYY')}</span>
4147
</div>

src/shared/containers/timeline-wall/styles.scss

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@
1717
@media (max-width: 768px) {
1818
min-height: 190px;
1919
}
20-
}
2120

22-
.header-admin {
23-
min-height: 207px;
21+
&.header-with-discuss {
22+
@media (max-width: 768px) {
23+
min-height: 214px;
24+
}
25+
}
26+
27+
&.header-admin {
28+
min-height: 207px;
29+
}
2430
}
2531

2632
.header-content-1 {
@@ -68,6 +74,40 @@
6874
display: none !important;
6975
}
7076

77+
.btn-discuss {
78+
margin-top: 11px;
79+
display: flex;
80+
align-items: center;
81+
position: absolute;
82+
top: 33px;
83+
left: 1408px;
84+
padding: 0;
85+
background: transparent;
86+
border: none;
87+
color: white;
88+
89+
@media (max-width: 1500px) {
90+
left: unset;
91+
right: 0;
92+
}
93+
94+
@media (max-width: 768px) {
95+
position: relative;
96+
right: unset;
97+
top: 0;
98+
margin-left: 27px;
99+
}
100+
101+
span {
102+
@include roboto-bold;
103+
104+
font-weight: 700;
105+
font-size: 16px;
106+
line-height: 16px;
107+
margin-right: 4px;
108+
}
109+
}
110+
71111
.tab-item {
72112
border: none;
73113
background-color: transparent;

0 commit comments

Comments
 (0)