From a51ea9b689f5df6a268e55028981c9badf5d1392 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 7 Dec 2016 12:47:29 +0530 Subject: [PATCH 01/20] Github issue #566, Connect Login/Logout redirect not working correctly in Safari -- Fixed. Reason was the missing # in URL. # is required for angular routes. --- src/config/constants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index 26cbe7e0e..f22e2bce9 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -180,8 +180,8 @@ export const PROJECT_ATTACHMENTS_FOLDER = process.env.PROJECT_ATTACHMENTS_FOLDER export const DOMAIN = process.env.domain || 'topcoder.com' export const CONNECT_DOMAIN = `connect.${DOMAIN}` export const ACCOUNTS_APP_CONNECTOR_URL = process.env.ACCOUNTS_APP_CONNECTOR_URL -export const ACCOUNTS_APP_LOGIN_URL = `https://accounts.${DOMAIN}/connect` -export const ACCOUNTS_APP_REGISTER_URL = `https://accounts.${DOMAIN}/connect/registration` +export const ACCOUNTS_APP_LOGIN_URL = `https://accounts.${DOMAIN}/#/connect` +export const ACCOUNTS_APP_REGISTER_URL = `https://accounts.${DOMAIN}/#/connect/registration` export const TC_API_URL = `https://api.${DOMAIN}` export const DIRECT_PROJECT_URL = `https://www.${DOMAIN}/direct/projectOverview?formData.projectId=` From 396cd01a6cdb9ea9a171fec1e07087bc851d517d Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 8 Dec 2016 13:04:28 +0530 Subject: [PATCH 02/20] Github #166, Messaging: unposted content alert -- Implemented the required behaviour with Discussions page. Took liberty to implement the same behaviour when user has unposted content and tries to change the thread from the left panel of thread list. --- src/components/Feed/NewPost.jsx | 27 ++++++--- src/projects/detail/Messages.jsx | 9 ++- .../detail/containers/MessagesContainer.js | 60 ++++++++++++++++++- 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/components/Feed/NewPost.jsx b/src/components/Feed/NewPost.jsx index 653e16c2e..e295b4716 100644 --- a/src/components/Feed/NewPost.jsx +++ b/src/components/Feed/NewPost.jsx @@ -41,12 +41,13 @@ class NewPost extends React.Component { constructor(props) { super(props) this.state = {editorState: EditorState.createEmpty(), expandedEditor: false, canSubmit: false} + this.onTitleChange = this.onTitleChange.bind(this) this.onEditorChange = this.onEditorChange.bind(this) this.handleKeyCommand = this.handleKeyCommand.bind(this) this.toggleBlockType = this.toggleBlockType.bind(this) this.toggleInlineStyle = this.toggleInlineStyle.bind(this) this.onClickOutside = this.onClickOutside.bind(this) - this.onNewPostChange = this.onNewPostChange.bind(this) + this.validateSubmitState = this.validateSubmitState.bind(this) } componentDidMount() { @@ -59,11 +60,11 @@ class NewPost extends React.Component { } componentWillReceiveProps(nextProps) { - if (!(nextProps.isCreating || nextProps.hasError && !nextProps.isCreating)) { + if (nextProps.isCreating !== this.props.isCreating && !nextProps.isCreating && !nextProps.hasError) { this.setState({editorState: EditorState.createEmpty()}) this.refs.title.value = '' } - this.onNewPostChange() + this.validateSubmitState() } onClickOutside(evt) { @@ -125,15 +126,27 @@ class NewPost extends React.Component { onEditorChange(editorState) { this.setState({editorState}) - this.onNewPostChange() + this.validateSubmitState() + if (this.props.onNewPostChange) { + this.props.onNewPostChange(this.refs.title.value, stateToMarkdown(editorState.getCurrentContent())) + } } - onNewPostChange() { + validateSubmitState() { + const { editorState } = this.state this.setState({ - canSubmit: this.refs.title && !!this.refs.title.value.trim().length && this.state.editorState.getCurrentContent().hasText() + canSubmit: this.refs.title && !!this.refs.title.value.trim().length && editorState.getCurrentContent().hasText() }) } + onTitleChange() { + const { editorState } = this.state + this.validateSubmitState() + if (this.props.onNewPostChange) { + this.props.onNewPostChange(this.refs.title.value, stateToMarkdown(editorState.getCurrentContent())) + } + } + render() { const {currentUser, titlePlaceholder, isCreating} = this.props const {editorState, canSubmit} = this.state @@ -191,7 +204,7 @@ class NewPost extends React.Component { ref="title" className="new-post-title" type="text" - onChange={this.onNewPostChange} + onChange={this.onTitleChange} placeholder={ titlePlaceholder || 'Title of the post'} />
diff --git a/src/projects/detail/Messages.jsx b/src/projects/detail/Messages.jsx index 3086400fa..a1e9f5a2a 100644 --- a/src/projects/detail/Messages.jsx +++ b/src/projects/detail/Messages.jsx @@ -4,7 +4,12 @@ import MessagesContainer from './containers/MessagesContainer' require('./Messages.scss') -const Messages = ({ location, project, currentMemberRole }) => ( - +const Messages = ({ location, project, currentMemberRole, route }) => ( + ) export default Messages diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index 83afdb8f6..0ec6f3ed7 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -1,5 +1,6 @@ import _ from 'lodash' import React from 'react' +import { withRouter } from 'react-router' import { connect } from 'react-redux' import update from 'react-addons-update' import MessageList from '../../../components/MessageList/MessageList' @@ -30,12 +31,27 @@ class MessagesView extends React.Component { constructor(props) { super(props) - this.state = { threads : [], activeThreadId : null, showEmptyState : true, showAll: []} + this.state = { + threads : [], + activeThreadId : null, + showEmptyState : true, + showAll: [], + newPost: {} + } this.onThreadSelect = this.onThreadSelect.bind(this) this.onShowAllComments = this.onShowAllComments.bind(this) this.onAddNewMessage = this.onAddNewMessage.bind(this) this.onNewMessageChange = this.onNewMessageChange.bind(this) this.onNewThread = this.onNewThread.bind(this) + this.onLeave = this.onLeave.bind(this) + this.isChanged = this.isChanged.bind(this) + this.onNewPostChange = this.onNewPostChange.bind(this) + this.changeThread = this.changeThread.bind(this) + } + + componentDidMount() { + this.props.router.setRouteLeaveHook(this.props.route, this.onLeave) + window.addEventListener('beforeunload', this.onLeave) } componentWillMount() { @@ -46,6 +62,24 @@ class MessagesView extends React.Component { this.init(nextProps) } + componentWillUnmount() { + window.removeEventListener('beforeunload', this.onLeave) + } + + // Notify user if they navigate away while the form is modified. + onLeave(e) { + if (this.isChanged()) { + return e.returnValue = 'You have uposted content. Are you sure you want to leave?' + } + } + + isChanged() { + const { newPost } = this.state + const hasMessage = !_.isUndefined(_.find(this.state.threads, (thread) => thread.newMessage && thread.newMessage.length)) + const hasThread = (newPost.title && !!newPost.title.trim().length) || ( newPost.content && !!newPost.content.trim().length) + return hasThread || hasMessage + } + mapFeed(feed, isActive, showAll = false) { const { allMembers } = this.props const item = _.pick(feed, ['id', 'date', 'read', 'tag', 'title', 'totalPosts', 'userId', 'reference', 'referenceId', 'postIds', 'isAddingComment', 'isLoadingComments', 'error']) @@ -137,15 +171,28 @@ class MessagesView extends React.Component { } onThreadSelect(thread) { + const unsavedContentMsg = this.onLeave({}) + if (unsavedContentMsg) { + const changeConfirmed = confirm(unsavedContentMsg) + if (changeConfirmed) { + this.changeThread(thread) + } + } else { + this.changeThread(thread) + } + } + + changeThread(thread) { this.setState({ isCreateNewMessage: false, + newPost: {}, activeThreadId: thread.id, threads: this.state.threads.map((item) => { if (item.isActive) { if (item.id === thread.id) { return item } - return {...item, isActive: false, messages: item.messages.map((msg) => ({...msg, unread: false}))} + return {...item, isActive: false, newMessage: '', messages: item.messages.map((msg) => ({...msg, unread: false}))} } if (item.id === thread.id) { return {...item, isActive: true, unreadCount: 0} @@ -155,6 +202,12 @@ class MessagesView extends React.Component { }) } + onNewPostChange(title, content) { + this.setState({ + newPost: {title, content} + }) + } + onNewMessageChange(content) { this.setState({ threads: this.state.threads.map((item) => { @@ -200,6 +253,7 @@ class MessagesView extends React.Component { !props.isLoading) -const EnhancedMessagesView = enhance(MessagesView) +const EnhancedMessagesView = withRouter(enhance(MessagesView)) class MessagesContainer extends React.Component { constructor(props) { From 6b3974be29e5cfe8b697ba90272a92ca927a44c5 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 8 Dec 2016 15:20:03 +0530 Subject: [PATCH 03/20] Github #166, Messaging: unposted content alert -- Implemented the required behaviour with Dashboard page. -- Tried to handle the warning of multiple leave hooks for the same route, however, the solution didn't work. Moving ahead as right now it is a warning and not causing any problem. May be we can launch a challenge to get it fixed. --- src/components/Feed/NewPost.jsx | 6 ++- src/projects/detail/Dashboard.jsx | 4 +- .../detail/containers/FeedContainer.js | 42 ++++++++++++++++++- .../detail/containers/MessagesContainer.js | 34 ++++++++++++++- 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/components/Feed/NewPost.jsx b/src/components/Feed/NewPost.jsx index e295b4716..37d5982ce 100644 --- a/src/components/Feed/NewPost.jsx +++ b/src/components/Feed/NewPost.jsx @@ -128,7 +128,8 @@ class NewPost extends React.Component { this.setState({editorState}) this.validateSubmitState() if (this.props.onNewPostChange) { - this.props.onNewPostChange(this.refs.title.value, stateToMarkdown(editorState.getCurrentContent())) + // NOTE: uses getPlainText method to avoid newline character for empty content + this.props.onNewPostChange(this.refs.title.value, editorState.getCurrentContent().getPlainText()) } } @@ -143,7 +144,8 @@ class NewPost extends React.Component { const { editorState } = this.state this.validateSubmitState() if (this.props.onNewPostChange) { - this.props.onNewPostChange(this.refs.title.value, stateToMarkdown(editorState.getCurrentContent())) + // NOTE: uses getPlainText method to avoid newline character for empty content + this.props.onNewPostChange(this.refs.title.value, editorState.getCurrentContent().getPlainText()) } } diff --git a/src/projects/detail/Dashboard.jsx b/src/projects/detail/Dashboard.jsx index 45d89c546..1279d4015 100644 --- a/src/projects/detail/Dashboard.jsx +++ b/src/projects/detail/Dashboard.jsx @@ -5,7 +5,7 @@ import Sticky from 'react-stickynode' require('./Dashboard.scss') -const Dashboard = ({project, currentMemberRole}) => ( +const Dashboard = ({project, currentMemberRole, route}) => (
@@ -16,7 +16,7 @@ const Dashboard = ({project, currentMemberRole}) => (
- +
diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index e14298aaf..5a1e21bab 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -1,4 +1,5 @@ import React, { PropTypes } from 'react' +import { withRouter } from 'react-router' import _ from 'lodash' import { THREAD_MESSAGES_PAGE_SIZE, @@ -36,7 +37,16 @@ class FeedView extends React.Component { this.onNewCommentChange = this.onNewCommentChange.bind(this) this.onShowAllComments = this.onShowAllComments.bind(this) this.onAddNewComment = this.onAddNewComment.bind(this) - this.state = { feeds : [], showAll: [] } + this.onLeave = this.onLeave.bind(this) + this.isChanged = this.isChanged.bind(this) + this.onNewPostChange = this.onNewPostChange.bind(this) + this.state = { feeds : [], showAll: [], newPost: {} } + } + + componentDidMount() { + const routeLeaveHook = this.props.router.setRouteLeaveHook(this.props.route, this.onLeave) + window.addEventListener('beforeunload', this.onLeave) + this.setState({ routeLeaveHook }) } componentWillMount() { @@ -47,6 +57,27 @@ class FeedView extends React.Component { this.init(nextProps) } + componentWillUnmount() { + if (this.state.routeLeaveHook) { + this.state.routeLeaveHook() + } + window.removeEventListener('beforeunload', this.onLeave) + } + + // Notify user if they navigate away while the form is modified. + onLeave(e) { + if (this.isChanged()) { + return e.returnValue = 'You have uposted content. Are you sure you want to leave?' + } + } + + isChanged() { + const { newPost } = this.state + const hasComment = !_.isUndefined(_.find(this.state.feeds, (feed) => feed.newComment && feed.newComment.length)) + const hasThread = (newPost.title && !!newPost.title.trim().length) || ( newPost.content && !!newPost.content.trim().length) + return hasThread || hasComment + } + mapFeed(feed, showAll = false) { const { allMembers } = this.props const item = _.pick(feed, ['id', 'date', 'read', 'tag', 'title', 'totalPosts', 'userId', 'reference', 'referenceId', 'postIds', 'isAddingComment', 'isLoadingComments', 'error']) @@ -99,6 +130,12 @@ class FeedView extends React.Component { }) } + onNewPostChange(title, content) { + this.setState({ + newPost: {title, content} + }) + } + onNewPost({title, content}) { const { project } = this.props const newFeed = { @@ -194,6 +231,7 @@ class FeedView extends React.Component { isCreating={ isCreatingFeed } hasError={ error } heading="NEW STATUS POST" + onNewPostChange={this.onNewPostChange} titlePlaceholder="Share the latest project updates with the team" /> } @@ -203,7 +241,7 @@ class FeedView extends React.Component { } } const enhance = spinnerWhileLoading(props => !props.isLoading) -const EnhancedFeedView = enhance(FeedView) +const EnhancedFeedView = withRouter(enhance(FeedView)) class FeedContainer extends React.Component { diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index 0ec6f3ed7..fc81d6c45 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -47,11 +47,14 @@ class MessagesView extends React.Component { this.isChanged = this.isChanged.bind(this) this.onNewPostChange = this.onNewPostChange.bind(this) this.changeThread = this.changeThread.bind(this) + this.onNewThreadClick = this.onNewThreadClick.bind(this) + this.showNewThreadForm = this.showNewThreadForm.bind(this) } componentDidMount() { - this.props.router.setRouteLeaveHook(this.props.route, this.onLeave) + const routeLeaveHook = this.props.router.setRouteLeaveHook(this.props.route, this.onLeave) window.addEventListener('beforeunload', this.onLeave) + this.setState({ routeLeaveHook }) } componentWillMount() { @@ -64,6 +67,9 @@ class MessagesView extends React.Component { componentWillUnmount() { window.removeEventListener('beforeunload', this.onLeave) + if (this.state.routeLeaveHook) { + this.state.routeLeaveHook() + } } // Notify user if they navigate away while the form is modified. @@ -208,6 +214,30 @@ class MessagesView extends React.Component { }) } + onNewThreadClick() { + const unsavedContentMsg = this.onLeave({}) + if (unsavedContentMsg) { + const changeConfirmed = confirm(unsavedContentMsg) + if (changeConfirmed) { + this.showNewThreadForm() + } + } else { + this.showNewThreadForm() + } + } + + showNewThreadForm() { + this.setState({ + isCreateNewMessage: true, + threads: this.state.threads.map((item) => { + if (item.isActive) { + return {...item, newMessage: ''} + } + return item + }) + }) + } + onNewMessageChange(content) { this.setState({ threads: this.state.threads.map((item) => { @@ -281,7 +311,7 @@ class MessagesView extends React.Component {
this.setState({isCreateNewMessage: true})} + onAdd={ this.onNewThreadClick } threads={threads} onSelect={this.onThreadSelect} showAddButton={ !!currentMemberRole } From 68084290112534e3a1a10dd1f0649c2453bd5441 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 8 Dec 2016 15:35:57 +0530 Subject: [PATCH 04/20] Github issue #566, Connect Login/Logout redirect not working correctly in Safari -- Fixed logout issue in Safari --- src/components/TopBar/TopBar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TopBar/TopBar.jsx b/src/components/TopBar/TopBar.jsx index 7373b9586..f370aa6b6 100644 --- a/src/components/TopBar/TopBar.jsx +++ b/src/components/TopBar/TopBar.jsx @@ -53,7 +53,7 @@ class TopBar extends Component { isPowerUser, loginUrl, registerUrl, isFilterVisible } = this.props const homePageUrl = window.location.protocol + '//' + window.location.hostname - const logoutLink = 'https://accounts.' + domain + '/logout?retUrl=' + homePageUrl + const logoutLink = 'https://accounts.' + domain + '/#/logout?retUrl=' + homePageUrl const isLoggedIn = !!userHandle const logoTargetUrl = isLoggedIn ? '/projects' : '/' From 94ee808dcc9c6ece4e63f5869d973d0f51113b87 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 8 Dec 2016 16:05:03 +0530 Subject: [PATCH 05/20] Github issue #360, Even though user clear the username from the textbox using (x) button, Can add the user by clicking the ADD button -- Removed X icon for IE 11 --- src/components/TeamManagement/TeamManagement.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/TeamManagement/TeamManagement.scss b/src/components/TeamManagement/TeamManagement.scss index d5ad39582..cbab61f3c 100644 --- a/src/components/TeamManagement/TeamManagement.scss +++ b/src/components/TeamManagement/TeamManagement.scss @@ -214,6 +214,10 @@ $tc-body-extra-small : 12px; } } + input::-ms-clear { + display: none; + } + .modal-inline-form{ display: flex; margin-bottom: $base-unit*2; From 2d470f75d3389ccfc512404605e30dc12ab00b57 Mon Sep 17 00:00:00 2001 From: Filipp Nisenzoun Date: Thu, 8 Dec 2016 17:26:23 -0800 Subject: [PATCH 06/20] Updating footer Help link Updating footer Help link to point to Help Center FAQ instead of general help page --- src/components/FooterV2/FooterV2.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FooterV2/FooterV2.jsx b/src/components/FooterV2/FooterV2.jsx index 48ef8a26b..1fd5bb273 100644 --- a/src/components/FooterV2/FooterV2.jsx +++ b/src/components/FooterV2/FooterV2.jsx @@ -6,7 +6,7 @@ const FooterV2 = () => ( From b07025e17c647be0e574f591632515137eb5b0d4 Mon Sep 17 00:00:00 2001 From: Filipp Nisenzoun Date: Thu, 8 Dec 2016 17:28:04 -0800 Subject: [PATCH 07/20] Updating footer Help link Updating footer Help link to point to new Connect FAQ instead of general help page --- src/components/Footer/Footer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Footer/Footer.jsx b/src/components/Footer/Footer.jsx index 70876b994..7c0b0ddb0 100644 --- a/src/components/Footer/Footer.jsx +++ b/src/components/Footer/Footer.jsx @@ -9,7 +9,7 @@ const Footer = () => { const otherNavigationItems = [ {img: '', text: 'About', link: 'https://www.topcoder.com/about-topcoder/', target: '_blank'}, {img: '', text: 'Contact', link: 'https://www.topcoder.com/about-topcoder/contact/', target: '_blank'}, - {img: '', text: 'Help', link: 'https://help.topcoder.com/hc/en-us', target: '_blank'}, + {img: '', text: 'Help', link: 'https://help.topcoder.com/hc/en-us/articles/225540188-Topcoder-Connect-FAQs', target: '_blank'}, {img: '', text: 'Privacy', link: 'https://www.topcoder.com/community/how-it-works/privacy-policy/', target: '_blank'}, {img: '', text: 'Terms', link: 'https://connect.topcoder.com/terms'} ] From 623903b1ef61df8a66b8668b098b48cd86812250 Mon Sep 17 00:00:00 2001 From: Victor George Date: Fri, 9 Dec 2016 11:52:23 -0800 Subject: [PATCH 08/20] Switch the Beta logo with the original Connect logo --- src/components/TopBar/TopBar.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TopBar/TopBar.jsx b/src/components/TopBar/TopBar.jsx index f370aa6b6..c5bde73a4 100644 --- a/src/components/TopBar/TopBar.jsx +++ b/src/components/TopBar/TopBar.jsx @@ -6,7 +6,7 @@ import cn from 'classnames' import _ from 'lodash' import { UserDropdown, Icons } from 'appirio-tech-react-components' -const { ConnectLogoBeta } = Icons +const { ConnectLogo } = Icons import { SearchBar } from 'appirio-tech-react-components' import Filters from './Filters' import ProjectToolBar from './ProjectToolBar' @@ -67,7 +67,7 @@ class TopBar extends Component { ] const logo = (
- +
) const avatar = ( From 4dc883c11564095b9bb625dccd3de41c93ae0d7a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 12 Dec 2016 12:00:12 +0530 Subject: [PATCH 09/20] Github issue #579, General: allow "admin" role to have same rights as "manager" role -- Aliased administrator roles to act like as connect manager --- .../detail/containers/TeamManagementContainer.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/projects/detail/containers/TeamManagementContainer.jsx b/src/projects/detail/containers/TeamManagementContainer.jsx index 57383841c..edbd5c67e 100644 --- a/src/projects/detail/containers/TeamManagementContainer.jsx +++ b/src/projects/detail/containers/TeamManagementContainer.jsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux' import { withRouter } from 'react-router' import _ from 'lodash' import { - ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, + ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, ROLE_ADMINISTRATOR, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_CUSTOMER, AUTOCOMPLETE_TRIGGER_LENGTH } from '../../../config/constants' import TeamManagement from '../../../components/TeamManagement/TeamManagement' @@ -221,13 +221,14 @@ class TeamManagementContainer extends Component { } const mapStateToProps = ({ loadUser, members }) => { + const powerUserRoles = [ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, ROLE_ADMINISTRATOR] + const managerRoles = [ ROLE_ADMINISTRATOR, ROLE_CONNECT_MANAGER ] return { currentUser: { userId: parseInt(loadUser.user.id), isCopilot: _.indexOf(loadUser.user.roles, ROLE_CONNECT_COPILOT) > -1, - isManager: _.indexOf(loadUser.user.roles, ROLE_CONNECT_MANAGER) > -1, - isCustomer: _.indexOf(loadUser.user.roles, ROLE_CONNECT_MANAGER) === -1 - && _.indexOf(loadUser.user.roles, ROLE_CONNECT_COPILOT) === -1 + isManager: loadUser.user.roles.some((role) => managerRoles.indexOf(role) !== -1), + isCustomer: !loadUser.user.roles.some((role) => powerUserRoles.indexOf(role) !== -1) }, allMembers: _.values(members.members) } From 8d0d7429fd39619001afc1b588a3e3294a99543c Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 12 Dec 2016 17:24:34 +0530 Subject: [PATCH 10/20] Github #166, Messaging: unposted content alert -- Fixed edge case where a reply to a topic was not being persisted after loading more posts for a topic --- src/projects/detail/containers/FeedContainer.js | 6 +++++- src/projects/detail/containers/MessagesContainer.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index 5a1e21bab..4a36eb3bb 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -78,7 +78,7 @@ class FeedView extends React.Component { return hasThread || hasComment } - mapFeed(feed, showAll = false) { + mapFeed(feed, showAll = false, resetNewComment = false) { const { allMembers } = this.props const item = _.pick(feed, ['id', 'date', 'read', 'tag', 'title', 'totalPosts', 'userId', 'reference', 'referenceId', 'postIds', 'isAddingComment', 'isLoadingComments', 'error']) if (isSystemUser(item.userId)) { @@ -117,6 +117,10 @@ class FeedView extends React.Component { }) } item.newComment = '' + if (!resetNewComment) { + const feedFromState = _.find(this.state.feeds, f => feed.id === f.id) + item.newComment = feedFromState ? feedFromState.newComment : '' + } item.hasMoreComments = item.comments.length !== item.totalComments return item } diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index fc81d6c45..a74d99a92 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -86,7 +86,7 @@ class MessagesView extends React.Component { return hasThread || hasMessage } - mapFeed(feed, isActive, showAll = false) { + mapFeed(feed, isActive, showAll = false, resetNewMessage = false) { const { allMembers } = this.props const item = _.pick(feed, ['id', 'date', 'read', 'tag', 'title', 'totalPosts', 'userId', 'reference', 'referenceId', 'postIds', 'isAddingComment', 'isLoadingComments', 'error']) item.isActive = isActive @@ -126,6 +126,10 @@ class MessagesView extends React.Component { }) } item.newMessage = '' + if (!resetNewMessage) { + const threadFromState = _.find(this.state.threads, t => feed.id === t.id) + item.newMessage = threadFromState ? threadFromState.newMessage : '' + } item.hasMoreMessages = item.messages.length < item.totalComments return item } From 2004ab6cd5605756084925adda10d4251b6315e4 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 12 Dec 2016 17:55:43 +0530 Subject: [PATCH 11/20] Github issue #574, Discussions: no footer on Discussion tab -- Added footer for the discussion page --- src/projects/detail/containers/MessagesContainer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index a74d99a92..2bc1f1ae9 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -10,6 +10,7 @@ import NewPost from '../../../components/Feed/NewPost' import { laodProjectMessages, createProjectTopic, loadFeedComments, addFeedComment } from '../../actions/projectTopics' import spinnerWhileLoading from '../../../components/LoadingSpinner' import {FullHeightContainer} from 'appirio-tech-react-components' +import FooterV2 from '../../../components/FooterV2/FooterV2' import { THREAD_MESSAGES_PAGE_SIZE, @@ -322,6 +323,7 @@ class MessagesView extends React.Component { showEmptyState={ showEmptyState && !threads.length } scrollPosition={ scrollPosition } /> +
{ (showEmptyState && !threads.length) && From f8f3ae9f12eba88e75ee924edce956306f67cfb1 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 13 Dec 2016 12:42:53 +0530 Subject: [PATCH 12/20] Github issue #574, Discussions: no footer on Discussion tab -- Adjusted height of the left panel to accommodate the footer. --- src/components/MessageList/MessageList.jsx | 5 +++-- src/projects/detail/Messages.scss | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/MessageList/MessageList.jsx b/src/components/MessageList/MessageList.jsx index cbd89a0c1..cd85229ec 100644 --- a/src/components/MessageList/MessageList.jsx +++ b/src/components/MessageList/MessageList.jsx @@ -57,8 +57,9 @@ class MessageList extends Component { componentDidMount() { const { scrollPosition } = this.props const panelMessages = this.refs.panelMessages - // 145 = 60 for topbar + 45 for panel title + 20px for margin between topbar and left panel + 10px padding - panelMessages.style.height = (window.innerHeight - 145) + 'px' + // 215 = 60 for topbar + 45 for panel title + 20px for margin between topbar and left panel + 10px padding + // + 60px footer + 10px margin bw footer and left panel + panelMessages.style.height = (window.innerHeight - 215) + 'px' if (scrollPosition) { // We use requestAnimationFrame because this function may be executed before // the DOM elements are actually drawn. diff --git a/src/projects/detail/Messages.scss b/src/projects/detail/Messages.scss index be8e80b37..532e2fa7a 100644 --- a/src/projects/detail/Messages.scss +++ b/src/projects/detail/Messages.scss @@ -18,7 +18,7 @@ @include flexBox; max-width: 1110px; margin: 20px auto; - height: calc(100% - 20px);// 20px is for bottom margin + height: calc(100% - 80px);// 20px is for bottom margin, 60px for footer .left-area { @include flexWidth(1); From 96a3b87a58df2f7f7a62485039857264a95db190 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 13 Dec 2016 16:28:23 +0530 Subject: [PATCH 13/20] Github issue #568, Dashboard: status update thread is too "sticky" -- Cleared the redux state on successful projects load action --- src/projects/detail/Dashboard.jsx | 25 +++++++++++++++++-- .../detail/containers/ProjectInfoContainer.js | 2 +- src/projects/reducers/project.js | 4 ++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/projects/detail/Dashboard.jsx b/src/projects/detail/Dashboard.jsx index 1279d4015..f7cf3c8fe 100644 --- a/src/projects/detail/Dashboard.jsx +++ b/src/projects/detail/Dashboard.jsx @@ -1,11 +1,13 @@ import React from 'react' +import { connect } from 'react-redux' import ProjectInfoContainer from './containers/ProjectInfoContainer' import FeedContainer from './containers/FeedContainer' import Sticky from 'react-stickynode' +import spinnerWhileLoading from '../../components/LoadingSpinner' require('./Dashboard.scss') -const Dashboard = ({project, currentMemberRole, route}) => ( +const DashboardView = ({project, currentMemberRole, route}) => (
@@ -22,4 +24,23 @@ const Dashboard = ({project, currentMemberRole, route}) => (
) -export default Dashboard +const enhance = spinnerWhileLoading(props => !props.isLoading) +const EnhancedDashboardView = enhance(DashboardView) + +class Dashboard extends React.Component { + constructor(props) { + super(props) + } + + render() { + return + } +} + +const mapStateToProps = ({ projectState, projectTopics }) => { + return { + isLoading : projectState.isLoading + } +} + +export default connect(mapStateToProps)(Dashboard) diff --git a/src/projects/detail/containers/ProjectInfoContainer.js b/src/projects/detail/containers/ProjectInfoContainer.js index 36fce9b24..d4220c89b 100644 --- a/src/projects/detail/containers/ProjectInfoContainer.js +++ b/src/projects/detail/containers/ProjectInfoContainer.js @@ -135,7 +135,7 @@ class ProjectInfoContainer extends React.Component { currentMemberRole={currentMemberRole} description={project.description} type={project.type} - devices={project.details.devices || []} + devices={ _.get(project, 'details.devices', []) } status={project.status} onChangeStatus={this.onChangeStatus} duration={duration} budget={budget} diff --git a/src/projects/reducers/project.js b/src/projects/reducers/project.js index 1791b363f..e1523a823 100644 --- a/src/projects/reducers/project.js +++ b/src/projects/reducers/project.js @@ -9,7 +9,8 @@ import { REMOVE_PROJECT_ATTACHMENT_PENDING, REMOVE_PROJECT_ATTACHMENT_SUCCESS, REMOVE_PROJECT_ATTACHMENT_FAILURE, ADD_PROJECT_MEMBER_PENDING, ADD_PROJECT_MEMBER_SUCCESS, ADD_PROJECT_MEMBER_FAILURE, UPDATE_PROJECT_MEMBER_PENDING, UPDATE_PROJECT_MEMBER_SUCCESS, UPDATE_PROJECT_MEMBER_FAILURE, - REMOVE_PROJECT_MEMBER_PENDING, REMOVE_PROJECT_MEMBER_SUCCESS, REMOVE_PROJECT_MEMBER_FAILURE + REMOVE_PROJECT_MEMBER_PENDING, REMOVE_PROJECT_MEMBER_SUCCESS, REMOVE_PROJECT_MEMBER_FAILURE, + GET_PROJECTS_SUCCESS } from '../../config/constants' import _ from 'lodash' import update from 'react-addons-update' @@ -50,6 +51,7 @@ export const projectState = function (state=initialState, action) { }) case CLEAR_LOADED_PROJECT: + case GET_PROJECTS_SUCCESS: return Object.assign({}, state, { project: {} }) From f20de04ee0c3022613e0fcb6315c075c59a151f9 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 13 Dec 2016 16:34:23 +0530 Subject: [PATCH 14/20] Fixed lint error --- src/projects/detail/Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/projects/detail/Dashboard.jsx b/src/projects/detail/Dashboard.jsx index f7cf3c8fe..277d41e96 100644 --- a/src/projects/detail/Dashboard.jsx +++ b/src/projects/detail/Dashboard.jsx @@ -37,7 +37,7 @@ class Dashboard extends React.Component { } } -const mapStateToProps = ({ projectState, projectTopics }) => { +const mapStateToProps = ({ projectState }) => { return { isLoading : projectState.isLoading } From dd08081da5e8736c1808d8b9c40f5ddfdf9d8e41 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 14 Dec 2016 12:18:57 +0530 Subject: [PATCH 15/20] Github issue #572, Messaging: Owner response post missing from status update -- Removed the constraint of user to be a team member of the project in order to view the comments of status updates. Further, I am assuming we need to do the same for Discussions tab as well, so removing the constraint there as well. So, now any user who has the access to the project's dashboard and discussions pages, can view posts and comments. --- src/projects/detail/containers/FeedContainer.js | 2 +- src/projects/detail/containers/MessagesContainer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index 4a36eb3bb..40ddc4ad4 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -212,7 +212,7 @@ class FeedView extends React.Component {
Date: Wed, 14 Dec 2016 12:42:36 +0530 Subject: [PATCH 16/20] Github issue #572, Messaging: Owner response post missing from status update -- Reverted previous changes. Found the root cause of the problem. Actually the flag, to prevent non team member from commenting on posts, was wrongly being used to render the feed's comments section on dashboard. It is used correctly on Discussions page. --- src/components/Feed/Feed.jsx | 4 ++-- src/components/Feed/FeedComments.jsx | 5 +++-- src/projects/detail/containers/FeedContainer.js | 2 +- src/projects/detail/containers/MessagesContainer.js | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/Feed/Feed.jsx b/src/components/Feed/Feed.jsx index 963cbcdfc..168ee7bb5 100644 --- a/src/components/Feed/Feed.jsx +++ b/src/components/Feed/Feed.jsx @@ -36,8 +36,8 @@ const Feed = (props) => {
- {allowComments && { avatarUrl={currentUser.photoURL} comments={comments} isAddingComment={ isAddingComment } - />} + /> {children} ) diff --git a/src/components/Feed/FeedComments.jsx b/src/components/Feed/FeedComments.jsx index d8e93b8fd..ec8da7997 100644 --- a/src/components/Feed/FeedComments.jsx +++ b/src/components/Feed/FeedComments.jsx @@ -25,7 +25,7 @@ class FeedComments extends React.Component { render() { const { comments, currentUser, totalComments, onLoadMoreComments, isLoadingComments, hasMoreComments, onAdd, - onChange, content, avatarUrl, isAddingComment + onChange, content, avatarUrl, isAddingComment, allowComments } = this.props let authorName = currentUser.firstName if (authorName && currentUser.lastName) { @@ -77,6 +77,7 @@ class FeedComments extends React.Component {
)} + {allowComments && + />}
) } diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index 40ddc4ad4..4a36eb3bb 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -212,7 +212,7 @@ class FeedView extends React.Component {
Date: Thu, 15 Dec 2016 12:21:20 +0530 Subject: [PATCH 17/20] Github issue #574, Discussions: no footer on Discussion tab -- Fixed rendering issue of the footer for the discussions tab. Thanks @vic-appirio --- src/projects/detail/Messages.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/projects/detail/Messages.scss b/src/projects/detail/Messages.scss index 532e2fa7a..201dcdfa4 100644 --- a/src/projects/detail/Messages.scss +++ b/src/projects/detail/Messages.scss @@ -24,6 +24,7 @@ @include flexWidth(1); max-width: 360px; z-index: 14;/* Don't know the exact reason, but it needs explicit z-index to get behind the topbar*/ + transform: translate3d(0px, 0px, 0px); } .right-area { @include flexWidth(2); From d68e817aa6118b06d61e0a65112d9e6b817c3b51 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 15 Dec 2016 12:36:37 +0530 Subject: [PATCH 18/20] Github issue#561, General: Redirect connectv2.topcoder.com to connect.topcoder.com -- Added redirect to connect from connectv2 --- src/routes.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/routes.jsx b/src/routes.jsx index 67ebc1384..d30c7a702 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -17,6 +17,14 @@ const LoginRedirect = withProps({ redirectTo: `${ACCOUNTS_APP_LOGIN_URL}?retUrl=${window.location.protocol}//${window.location.hostname}${window.location.port ? ':' + window.location.port : ''}` })(RedirectComponent) +const redirectToConnect = (nextState, replace, callback) => { + if(window.location.hostname.indexOf('connectv2') === 0) { + window.location.assign(window.location.href.replace('connectv2', 'connect')) + return + } + callback() +} + const redirectToProject = (nextState, replace, callback) => { const feedId = nextState.params.feedId getFreshToken().then(() => { @@ -50,7 +58,7 @@ const redirectToProject = (nextState, replace, callback) => { } export default ( - window.scrollTo(0, 0)} component={ App }> + window.scrollTo(0, 0)} component={ App } onEnter={ redirectToConnect }> From 0c6fc6ea11fde713125aaf6bc64464644386d8d2 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 15 Dec 2016 14:43:09 +0530 Subject: [PATCH 19/20] Github issue#587, Comments Sort order within a topic is incorrect -- Fixed. We were missing sorting the posts when loading more posts for a feed --- src/projects/reducers/projectTopics.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/projects/reducers/projectTopics.js b/src/projects/reducers/projectTopics.js index 27ee4ee96..c3fa25651 100644 --- a/src/projects/reducers/projectTopics.js +++ b/src/projects/reducers/projectTopics.js @@ -157,6 +157,7 @@ export const projectTopics = function (state=initialState, action) { posts: { $push: payload.posts }, isLoadingComments: { $set : false } }) + updatedFeed.posts = _.sortBy(updatedFeed.posts, ['id']) const feedUpdateQuery = {} feedUpdateQuery[tag] = { topics: { $splice: [[feedIndex, 1, updatedFeed]] } } // update the state From b861468e3591615eb5b22b9361892bbc78c443eb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 15 Dec 2016 14:49:10 +0530 Subject: [PATCH 20/20] Github issue#585, Ignore 'user-joined' type posts in Topic comments -- Ignored empty posts whose author are 'system' --- src/projects/detail/containers/FeedContainer.js | 7 +++++-- src/projects/detail/containers/MessagesContainer.js | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js index 4a36eb3bb..f621ca219 100644 --- a/src/projects/detail/containers/FeedContainer.js +++ b/src/projects/detail/containers/FeedContainer.js @@ -103,15 +103,18 @@ class FeedView extends React.Component { author: isSystemUser(p.userId) ? SYSTEM_USER : allMembers[p.userId] } } + const validPost = (post) => { + return post.type === 'post' && (post.body && post.body.trim().length || !isSystemUser(post.userId)) + } if (showAll) { // if we are showing all comments, just iterate through the entire array _.forEach(_.slice(feed.posts, 1), p => { - p.type === 'post' ? item.comments.push(_toComment(p)) : item.totalComments-- + validPost(p) ? item.comments.push(_toComment(p)) : item.totalComments-- }) } else { // otherwise iterate from right and add to the beginning of the array _.forEachRight(_.slice(feed.posts, 1), (p) => { - p.type === 'post' ? item.comments.unshift(_toComment(p)) : item.totalComments-- + validPost(p) ? item.comments.unshift(_toComment(p)) : item.totalComments-- if (!feed.showAll && item.comments.length === THREAD_MESSAGES_PAGE_SIZE) return false }) diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index 2bc1f1ae9..6b3455a84 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -113,15 +113,18 @@ class MessagesView extends React.Component { author: isSystemUser(p.userId) ? SYSTEM_USER : allMembers[p.userId] } } + const validPost = (post) => { + return post.type === 'post' && (post.body && post.body.trim().length || !isSystemUser(post.userId)) + } if (showAll) { // if we are showing all comments, just iterate through the entire array _.forEach(feed.posts, p => { - p.type === 'post' ? item.messages.push(_toComment(p)) : item.totalComments-- + validPost(p) ? item.messages.push(_toComment(p)) : item.totalComments-- }) } else { // otherwise iterate from right and add to the beginning of the array _.forEachRight(feed.posts, (p) => { - p.type === 'post' ? item.messages.unshift(_toComment(p)) : item.totalComments-- + validPost(p) ? item.messages.unshift(_toComment(p)) : item.totalComments-- if (!feed.showAll && item.messages.length === THREAD_MESSAGES_PAGE_SIZE) return false })