Skip to content

Commit d4d59a6

Browse files
author
vikasrohit
authored
Merge pull request #569 from appirio-tech/feature/discussions_unposted_content_alert
Feature/discussions unposted content alert
2 parents 9fc4a7a + 6b3974b commit d4d59a6

File tree

5 files changed

+159
-17
lines changed

5 files changed

+159
-17
lines changed

src/components/Feed/NewPost.jsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ class NewPost extends React.Component {
4141
constructor(props) {
4242
super(props)
4343
this.state = {editorState: EditorState.createEmpty(), expandedEditor: false, canSubmit: false}
44+
this.onTitleChange = this.onTitleChange.bind(this)
4445
this.onEditorChange = this.onEditorChange.bind(this)
4546
this.handleKeyCommand = this.handleKeyCommand.bind(this)
4647
this.toggleBlockType = this.toggleBlockType.bind(this)
4748
this.toggleInlineStyle = this.toggleInlineStyle.bind(this)
4849
this.onClickOutside = this.onClickOutside.bind(this)
49-
this.onNewPostChange = this.onNewPostChange.bind(this)
50+
this.validateSubmitState = this.validateSubmitState.bind(this)
5051
}
5152

5253
componentDidMount() {
@@ -59,11 +60,11 @@ class NewPost extends React.Component {
5960
}
6061

6162
componentWillReceiveProps(nextProps) {
62-
if (!(nextProps.isCreating || nextProps.hasError && !nextProps.isCreating)) {
63+
if (nextProps.isCreating !== this.props.isCreating && !nextProps.isCreating && !nextProps.hasError) {
6364
this.setState({editorState: EditorState.createEmpty()})
6465
this.refs.title.value = ''
6566
}
66-
this.onNewPostChange()
67+
this.validateSubmitState()
6768
}
6869

6970
onClickOutside(evt) {
@@ -125,15 +126,29 @@ class NewPost extends React.Component {
125126

126127
onEditorChange(editorState) {
127128
this.setState({editorState})
128-
this.onNewPostChange()
129+
this.validateSubmitState()
130+
if (this.props.onNewPostChange) {
131+
// NOTE: uses getPlainText method to avoid newline character for empty content
132+
this.props.onNewPostChange(this.refs.title.value, editorState.getCurrentContent().getPlainText())
133+
}
129134
}
130135

131-
onNewPostChange() {
136+
validateSubmitState() {
137+
const { editorState } = this.state
132138
this.setState({
133-
canSubmit: this.refs.title && !!this.refs.title.value.trim().length && this.state.editorState.getCurrentContent().hasText()
139+
canSubmit: this.refs.title && !!this.refs.title.value.trim().length && editorState.getCurrentContent().hasText()
134140
})
135141
}
136142

143+
onTitleChange() {
144+
const { editorState } = this.state
145+
this.validateSubmitState()
146+
if (this.props.onNewPostChange) {
147+
// NOTE: uses getPlainText method to avoid newline character for empty content
148+
this.props.onNewPostChange(this.refs.title.value, editorState.getCurrentContent().getPlainText())
149+
}
150+
}
151+
137152
render() {
138153
const {currentUser, titlePlaceholder, isCreating} = this.props
139154
const {editorState, canSubmit} = this.state
@@ -191,7 +206,7 @@ class NewPost extends React.Component {
191206
ref="title"
192207
className="new-post-title"
193208
type="text"
194-
onChange={this.onNewPostChange}
209+
onChange={this.onTitleChange}
195210
placeholder={ titlePlaceholder || 'Title of the post'}
196211
/>
197212
<div className="draftjs-editor tc-textarea">

src/projects/detail/Dashboard.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Sticky from 'react-stickynode'
55

66
require('./Dashboard.scss')
77

8-
const Dashboard = ({project, currentMemberRole}) => (
8+
const Dashboard = ({project, currentMemberRole, route}) => (
99
<div>
1010
<div className="dashboard-container">
1111
<div className="left-area">
@@ -16,7 +16,7 @@ const Dashboard = ({project, currentMemberRole}) => (
1616
</Sticky>
1717
</div>
1818
<div className="right-area">
19-
<FeedContainer currentMemberRole={currentMemberRole} project={project} />
19+
<FeedContainer currentMemberRole={currentMemberRole} project={project} route={route} />
2020
</div>
2121
</div>
2222
</div>

src/projects/detail/Messages.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import MessagesContainer from './containers/MessagesContainer'
44

55
require('./Messages.scss')
66

7-
const Messages = ({ location, project, currentMemberRole }) => (
8-
<MessagesContainer location={ location } project={ project } currentMemberRole={ currentMemberRole } />
7+
const Messages = ({ location, project, currentMemberRole, route }) => (
8+
<MessagesContainer
9+
location={ location }
10+
project={ project }
11+
currentMemberRole={ currentMemberRole }
12+
route={ route }
13+
/>
914
)
1015
export default Messages

src/projects/detail/containers/FeedContainer.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { PropTypes } from 'react'
2+
import { withRouter } from 'react-router'
23
import _ from 'lodash'
34
import {
45
THREAD_MESSAGES_PAGE_SIZE,
@@ -36,7 +37,16 @@ class FeedView extends React.Component {
3637
this.onNewCommentChange = this.onNewCommentChange.bind(this)
3738
this.onShowAllComments = this.onShowAllComments.bind(this)
3839
this.onAddNewComment = this.onAddNewComment.bind(this)
39-
this.state = { feeds : [], showAll: [] }
40+
this.onLeave = this.onLeave.bind(this)
41+
this.isChanged = this.isChanged.bind(this)
42+
this.onNewPostChange = this.onNewPostChange.bind(this)
43+
this.state = { feeds : [], showAll: [], newPost: {} }
44+
}
45+
46+
componentDidMount() {
47+
const routeLeaveHook = this.props.router.setRouteLeaveHook(this.props.route, this.onLeave)
48+
window.addEventListener('beforeunload', this.onLeave)
49+
this.setState({ routeLeaveHook })
4050
}
4151

4252
componentWillMount() {
@@ -47,6 +57,27 @@ class FeedView extends React.Component {
4757
this.init(nextProps)
4858
}
4959

60+
componentWillUnmount() {
61+
if (this.state.routeLeaveHook) {
62+
this.state.routeLeaveHook()
63+
}
64+
window.removeEventListener('beforeunload', this.onLeave)
65+
}
66+
67+
// Notify user if they navigate away while the form is modified.
68+
onLeave(e) {
69+
if (this.isChanged()) {
70+
return e.returnValue = 'You have uposted content. Are you sure you want to leave?'
71+
}
72+
}
73+
74+
isChanged() {
75+
const { newPost } = this.state
76+
const hasComment = !_.isUndefined(_.find(this.state.feeds, (feed) => feed.newComment && feed.newComment.length))
77+
const hasThread = (newPost.title && !!newPost.title.trim().length) || ( newPost.content && !!newPost.content.trim().length)
78+
return hasThread || hasComment
79+
}
80+
5081
mapFeed(feed, showAll = false) {
5182
const { allMembers } = this.props
5283
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 {
99130
})
100131
}
101132

133+
onNewPostChange(title, content) {
134+
this.setState({
135+
newPost: {title, content}
136+
})
137+
}
138+
102139
onNewPost({title, content}) {
103140
const { project } = this.props
104141
const newFeed = {
@@ -194,6 +231,7 @@ class FeedView extends React.Component {
194231
isCreating={ isCreatingFeed }
195232
hasError={ error }
196233
heading="NEW STATUS POST"
234+
onNewPostChange={this.onNewPostChange}
197235
titlePlaceholder="Share the latest project updates with the team"
198236
/>
199237
}
@@ -203,7 +241,7 @@ class FeedView extends React.Component {
203241
}
204242
}
205243
const enhance = spinnerWhileLoading(props => !props.isLoading)
206-
const EnhancedFeedView = enhance(FeedView)
244+
const EnhancedFeedView = withRouter(enhance(FeedView))
207245

208246

209247
class FeedContainer extends React.Component {

src/projects/detail/containers/MessagesContainer.js

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import _ from 'lodash'
22
import React from 'react'
3+
import { withRouter } from 'react-router'
34
import { connect } from 'react-redux'
45
import update from 'react-addons-update'
56
import MessageList from '../../../components/MessageList/MessageList'
@@ -30,12 +31,30 @@ class MessagesView extends React.Component {
3031

3132
constructor(props) {
3233
super(props)
33-
this.state = { threads : [], activeThreadId : null, showEmptyState : true, showAll: []}
34+
this.state = {
35+
threads : [],
36+
activeThreadId : null,
37+
showEmptyState : true,
38+
showAll: [],
39+
newPost: {}
40+
}
3441
this.onThreadSelect = this.onThreadSelect.bind(this)
3542
this.onShowAllComments = this.onShowAllComments.bind(this)
3643
this.onAddNewMessage = this.onAddNewMessage.bind(this)
3744
this.onNewMessageChange = this.onNewMessageChange.bind(this)
3845
this.onNewThread = this.onNewThread.bind(this)
46+
this.onLeave = this.onLeave.bind(this)
47+
this.isChanged = this.isChanged.bind(this)
48+
this.onNewPostChange = this.onNewPostChange.bind(this)
49+
this.changeThread = this.changeThread.bind(this)
50+
this.onNewThreadClick = this.onNewThreadClick.bind(this)
51+
this.showNewThreadForm = this.showNewThreadForm.bind(this)
52+
}
53+
54+
componentDidMount() {
55+
const routeLeaveHook = this.props.router.setRouteLeaveHook(this.props.route, this.onLeave)
56+
window.addEventListener('beforeunload', this.onLeave)
57+
this.setState({ routeLeaveHook })
3958
}
4059

4160
componentWillMount() {
@@ -46,6 +65,27 @@ class MessagesView extends React.Component {
4665
this.init(nextProps)
4766
}
4867

68+
componentWillUnmount() {
69+
window.removeEventListener('beforeunload', this.onLeave)
70+
if (this.state.routeLeaveHook) {
71+
this.state.routeLeaveHook()
72+
}
73+
}
74+
75+
// Notify user if they navigate away while the form is modified.
76+
onLeave(e) {
77+
if (this.isChanged()) {
78+
return e.returnValue = 'You have uposted content. Are you sure you want to leave?'
79+
}
80+
}
81+
82+
isChanged() {
83+
const { newPost } = this.state
84+
const hasMessage = !_.isUndefined(_.find(this.state.threads, (thread) => thread.newMessage && thread.newMessage.length))
85+
const hasThread = (newPost.title && !!newPost.title.trim().length) || ( newPost.content && !!newPost.content.trim().length)
86+
return hasThread || hasMessage
87+
}
88+
4989
mapFeed(feed, isActive, showAll = false) {
5090
const { allMembers } = this.props
5191
const item = _.pick(feed, ['id', 'date', 'read', 'tag', 'title', 'totalPosts', 'userId', 'reference', 'referenceId', 'postIds', 'isAddingComment', 'isLoadingComments', 'error'])
@@ -137,15 +177,28 @@ class MessagesView extends React.Component {
137177
}
138178

139179
onThreadSelect(thread) {
180+
const unsavedContentMsg = this.onLeave({})
181+
if (unsavedContentMsg) {
182+
const changeConfirmed = confirm(unsavedContentMsg)
183+
if (changeConfirmed) {
184+
this.changeThread(thread)
185+
}
186+
} else {
187+
this.changeThread(thread)
188+
}
189+
}
190+
191+
changeThread(thread) {
140192
this.setState({
141193
isCreateNewMessage: false,
194+
newPost: {},
142195
activeThreadId: thread.id,
143196
threads: this.state.threads.map((item) => {
144197
if (item.isActive) {
145198
if (item.id === thread.id) {
146199
return item
147200
}
148-
return {...item, isActive: false, messages: item.messages.map((msg) => ({...msg, unread: false}))}
201+
return {...item, isActive: false, newMessage: '', messages: item.messages.map((msg) => ({...msg, unread: false}))}
149202
}
150203
if (item.id === thread.id) {
151204
return {...item, isActive: true, unreadCount: 0}
@@ -155,6 +208,36 @@ class MessagesView extends React.Component {
155208
})
156209
}
157210

211+
onNewPostChange(title, content) {
212+
this.setState({
213+
newPost: {title, content}
214+
})
215+
}
216+
217+
onNewThreadClick() {
218+
const unsavedContentMsg = this.onLeave({})
219+
if (unsavedContentMsg) {
220+
const changeConfirmed = confirm(unsavedContentMsg)
221+
if (changeConfirmed) {
222+
this.showNewThreadForm()
223+
}
224+
} else {
225+
this.showNewThreadForm()
226+
}
227+
}
228+
229+
showNewThreadForm() {
230+
this.setState({
231+
isCreateNewMessage: true,
232+
threads: this.state.threads.map((item) => {
233+
if (item.isActive) {
234+
return {...item, newMessage: ''}
235+
}
236+
return item
237+
})
238+
})
239+
}
240+
158241
onNewMessageChange(content) {
159242
this.setState({
160243
threads: this.state.threads.map((item) => {
@@ -200,6 +283,7 @@ class MessagesView extends React.Component {
200283
<NewPost
201284
currentUser={currentUser}
202285
onPost={this.onNewThread}
286+
onNewPostChange={ this.onNewPostChange }
203287
isCreating={isCreatingFeed}
204288
hasError={error}
205289
heading="New Discussion Post"
@@ -227,7 +311,7 @@ class MessagesView extends React.Component {
227311
<div className="messages-container">
228312
<div className="left-area">
229313
<MessageList
230-
onAdd={() => this.setState({isCreateNewMessage: true})}
314+
onAdd={ this.onNewThreadClick }
231315
threads={threads}
232316
onSelect={this.onThreadSelect}
233317
showAddButton={ !!currentMemberRole }
@@ -251,7 +335,7 @@ class MessagesView extends React.Component {
251335
}
252336

253337
const enhance = spinnerWhileLoading(props => !props.isLoading)
254-
const EnhancedMessagesView = enhance(MessagesView)
338+
const EnhancedMessagesView = withRouter(enhance(MessagesView))
255339

256340
class MessagesContainer extends React.Component {
257341
constructor(props) {

0 commit comments

Comments
 (0)