Skip to content

Commit 8ee5337

Browse files
author
vikasrohit
authored
Merge pull request #3204 from appirio-tech/hotfix/redirect-comments
Hotfix/post release 2.4.13.6
2 parents 485096a + 7388ea2 commit 8ee5337

File tree

4 files changed

+81
-61
lines changed

4 files changed

+81
-61
lines changed

src/projects/detail/containers/DashboardContainer.jsx

+1-53
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import React from 'react'
88
import _ from 'lodash'
99
import { connect } from 'react-redux'
10-
import { Redirect, withRouter, Link } from 'react-router-dom'
11-
import Alert from 'react-s-alert'
10+
import { withRouter, Link } from 'react-router-dom'
1211
import './DashboardContainer.scss'
1312

1413
import {
@@ -70,14 +69,9 @@ class DashboardContainer extends React.Component {
7069

7170
this.state = {
7271
open: false,
73-
matchesTopicUrl: null,
74-
matchesPostUrl: null,
75-
topicIdForPost: null
7672
}
7773
this.onNotificationRead = this.onNotificationRead.bind(this)
7874
this.toggleDrawer = this.toggleDrawer.bind(this)
79-
80-
this.alertedFailedTopicRedirect = false
8175
}
8276

8377
onNotificationRead(notification) {
@@ -101,18 +95,6 @@ class DashboardContainer extends React.Component {
10195
})
10296
}
10397

104-
/*
105-
For redirecting old urls to new urls for topics and posts
106-
Old TOPIC: '/projects/{{projectId}}/#feed-{{topicId}}',
107-
Old POST: '/projects/{{projectId}}/#comment-{{postId}}',
108-
*/
109-
const matchesTopicUrl = location.hash.match(/#feed-(\d+)/)
110-
const matchesPostUrl = location.hash.match(/#comment-(\d+)/)
111-
this.setState({
112-
matchesPostUrl,
113-
matchesTopicUrl
114-
})
115-
11698
// if the user is a customer and its not a direct link to a particular phase
11799
// then by default expand all phases which are active
118100
if (_.isEmpty(location.hash) && this.props.isCustomerUser) {
@@ -130,40 +112,12 @@ class DashboardContainer extends React.Component {
130112
collapseAllProjectPhases()
131113
}
132114

133-
componentWillReceiveProps(nextProps) {
134-
const { isFeedsLoading } = nextProps
135-
const { matchesPostUrl } = this.state
136-
137-
// we need topicId for redirecting old post url (/projects/{{projectId}}/#comment-{{postId}})
138-
if (!isFeedsLoading && matchesPostUrl && !this.alertedFailedTopicRedirect) {
139-
const topicIdForPost = this.getTopicIdForPost(matchesPostUrl[1])
140-
this.setState({ topicIdForPost })
141-
this.alertFailedTopicRedirection(matchesPostUrl, topicIdForPost, isFeedsLoading)
142-
}
143-
}
144-
145115
toggleDrawer() {
146116
this.setState((prevState) => ({
147117
open: !prevState.open
148118
}))
149119
}
150120

151-
// Get topic id corresponding to the post that we're trying to redirect to
152-
getTopicIdForPost(postId) {
153-
const {feeds} = this.props
154-
const topic = feeds && feeds
155-
.find(feed => feed.posts.find(p => p.id === Number(postId)))
156-
return topic && topic.id
157-
}
158-
159-
// Alert user in case the post is not available / not accessible to him.
160-
alertFailedTopicRedirection(matchesPostUrl, topicIdForPost, isFeedsLoading) {
161-
if (matchesPostUrl && !topicIdForPost && !isFeedsLoading) {
162-
this.alertedFailedTopicRedirect = true
163-
Alert.error('Couldn\'t find the post')
164-
}
165-
}
166-
167121
render() {
168122
const {
169123
project,
@@ -191,9 +145,6 @@ class DashboardContainer extends React.Component {
191145
location,
192146
estimationQuestion,
193147
} = this.props
194-
const { matchesPostUrl, matchesTopicUrl, topicIdForPost } = this.state
195-
196-
197148
const projectTemplate = project && project.templateId && projectTemplates ? (getProjectTemplateById(projectTemplates, project.templateId)) : null
198149

199150
let template
@@ -254,9 +205,6 @@ class DashboardContainer extends React.Component {
254205
]}
255206
/>
256207

257-
{matchesTopicUrl && <Redirect to={`/projects/${project.id}/messages/${matchesTopicUrl[1]}`} />}
258-
{matchesPostUrl && topicIdForPost && <Redirect to={`/projects/${project.id}/messages/${topicIdForPost}#comment-${matchesPostUrl[1]}`} />}
259-
260208
<TwoColsLayout.Sidebar>
261209
<MediaQuery minWidth={SCREEN_BREAKPOINT_MD}>
262210
{(matches) => {

src/projects/detail/containers/MessagesTabContainer.jsx

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react'
22
import { connect } from 'react-redux'
3-
import { Prompt } from 'react-router-dom'
3+
import { Prompt, withRouter } from 'react-router-dom'
4+
import Alert from 'react-s-alert'
45
import MediaQuery from 'react-responsive'
56
import {
67
groupBy,
@@ -189,6 +190,22 @@ class MessagesTabContainer extends React.Component {
189190
}
190191

191192
componentWillReceiveProps(nextProps) {
193+
// if we have URL like `/projects/{projectId}/messages#comment-{postId}` without mentioning topicId
194+
// then we should try to find topic of such post and redirect to the full URL with topicId
195+
const matchesPostUrl = this.props.match.path === '/projects/:projectId/messages' &&
196+
this.props.location.hash.match(/#comment-(\d+)/)
197+
// as soon as all topics are loaded we will redirect to the correct URL, if there such topic
198+
if (this.props.isFeedsLoading && !nextProps.isFeedsLoading && matchesPostUrl) {
199+
const postId = parseInt(matchesPostUrl[1], 10)
200+
const topic = nextProps.feeds.find(feed => _.find(feed.posts, { id: postId }))
201+
202+
if (topic) {
203+
this.props.history.replace(`/projects/${this.props.match.params.projectId}/messages/${topic.id}#comment-${postId}`)
204+
} else {
205+
Alert.error('Couldn\'t find the post referred in URL')
206+
}
207+
}
208+
192209
// reset title and content in the state after successful post creation
193210
// so that we treat the post editor not changed, thus when we leave the page we don't get confirmation alert
194211
if (this.props.isCreatingFeed && !nextProps.isCreatingFeed && !nextProps.error) {
@@ -421,4 +438,4 @@ const mapDispatchToProps = {
421438
export default connect(
422439
mapStateToProps,
423440
mapDispatchToProps
424-
)(MessagesTabContainer)
441+
)(withRouter(MessagesTabContainer))

src/projects/reducers/projectTopics.js

+41-5
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ import {
3737
import update from 'react-addons-update'
3838
import { getLinksFromPost } from '../../helpers/draftJSHelper'
3939

40-
4140
const initialState = {
41+
// as we can load different kind of topics in parallel we have to track their loading status separately
42+
isLoadingPerTag: {
43+
MESSAGES: false,
44+
PRIMARY: false,
45+
},
46+
// `isLoading` would indicate the loading of any kind of topic as aggregation value for the values above
47+
// technically we could aggregate inside components, but as we already use `isLoading` in many places
48+
// so we do it inside the reducer for backward compatibility
4249
isLoading: false,
4350
isCreatingFeed: false,
4451
error: false,
@@ -49,6 +56,28 @@ const initialState = {
4956
}
5057
}
5158

59+
/**
60+
* Builds updated value of `isLoadingPerTag` state
61+
*
62+
* @param {Object} currentIsLoadingPerTag current `isLoadingPerTag` state
63+
* @param {String} tag topic tag
64+
* @param {Boolean} isLoading `true` if topic with such tag is loading now
65+
*
66+
* @returns {Object} update value for `isLoadingPerTag` state
67+
*/
68+
const updateIsLoadingPerTag = (currentIsLoadingPerTag, tag, isLoading) => ({
69+
...currentIsLoadingPerTag,
70+
[tag]: isLoading,
71+
})
72+
73+
/**
74+
* Calculates values for `isLoading` state value based on `isLoadingPerTag` state
75+
*
76+
* @param {Object} isLoadingPerTag `isLoadingPerTag` state
77+
*
78+
* @returns {Boolean} `true` if topic with any tag is loaded
79+
*/
80+
const getIsLoading = (isLoadingPerTag) => _.some(_.values(isLoadingPerTag))
5281

5382
export const projectTopics = function (state=initialState, action) {
5483
const payload = action.payload
@@ -63,9 +92,11 @@ export const projectTopics = function (state=initialState, action) {
6392
if (action.meta.projectId === state.projectId) {
6493
const feedUpdateQuery = {}
6594
feedUpdateQuery[action.meta.tag] = { $merge: { topics: [], totalCount: 0 } }
95+
const updatedIsLoadingPerTag = updateIsLoadingPerTag(state.isLoadingPerTag, action.meta.tag, true)
6696
return update(state, {
6797
feeds: feedUpdateQuery,
68-
isLoading: { $set: true },
98+
isLoadingPerTag: { $set: updatedIsLoadingPerTag },
99+
isLoading: { $set: getIsLoading(updatedIsLoadingPerTag) },
69100
error: { $set: false }
70101
})
71102
} else {
@@ -101,18 +132,23 @@ export const projectTopics = function (state=initialState, action) {
101132

102133
const feedUpdateQuery = {}
103134
feedUpdateQuery[action.meta.tag] = { $merge: { topics, totalCount: payload.totalCount } }
135+
const updatedIsLoadingPerTag = updateIsLoadingPerTag(state.isLoadingPerTag, action.meta.tag, false)
104136
return update(state, {
105-
isLoading: {$set: false},
137+
isLoadingPerTag: {$set: updatedIsLoadingPerTag},
138+
isLoading: {$set: getIsLoading(updatedIsLoadingPerTag)},
106139
error: {$set: false},
107140
feeds: feedUpdateQuery
108141
})
109142
}
110143
case LOAD_PROJECT_FEEDS_MEMBERS_FAILURE:
111-
case LOAD_PROJECT_FEEDS_FAILURE:
144+
case LOAD_PROJECT_FEEDS_FAILURE: {
145+
const updatedIsLoadingPerTag = updateIsLoadingPerTag(state.isLoadingPerTag, action.meta.tag, false)
112146
return Object.assign({}, initialState, {
113-
isLoading: false,
147+
isLoadingPerTag: {$set: updatedIsLoadingPerTag},
148+
isLoading: {$set: getIsLoading(updatedIsLoadingPerTag)},
114149
error: true
115150
})
151+
}
116152
case CREATE_PROJECT_FEED_PENDING:
117153
return Object.assign({}, state, {
118154
isCreatingFeed: true,

src/projects/routes.jsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,26 @@ const FileDownloadWithAuth = requiresAuthentication(FileDownload)
2121

2222
const ProjectDetailWithAuth = requiresAuthentication(() =>
2323
(<Switch>
24-
<Route exact path="/projects/:projectId" render={() => <ProjectDetail component={Dashboard} />} />
24+
<Route
25+
exact
26+
path="/projects/:projectId"
27+
render={({ match, location }) => {
28+
const matchesTopicUrl = location.hash.match(/#feed-(\d+)/)
29+
const matchesPostUrl = location.hash.match(/#comment-(\d+)/)
30+
31+
// redirect old Topic URLs to the topics on the messages tab
32+
if (matchesTopicUrl) {
33+
return <Redirect to={`/projects/${match.params.projectId}/messages/${matchesTopicUrl[1]}`} />
34+
35+
// redirect old Posts URLs to the messages tab
36+
// as we don't know the topic ID form the URL, message tab should take care about it
37+
} else if (matchesPostUrl) {
38+
return <Redirect to={`/projects/${match.params.projectId}/messages${location.hash}`} />
39+
}
40+
41+
return <ProjectDetail component={Dashboard} />
42+
}}
43+
/>
2544
<Route path="/projects/:projectId/status/:statusId" render={() => <ProjectDetail component={Dashboard} />} />
2645
<Route path="/projects/:projectId/messages/:topicId" render={() => <ProjectDetail component={MessagesTabContainer} />} />
2746
<Route path="/projects/:projectId/messages" render={() => <ProjectDetail component={MessagesTabContainer} />} />

0 commit comments

Comments
 (0)