Skip to content

Commit

Permalink
Merge pull request #10 from politics-rewired/politics-rewired/add-adm…
Browse files Browse the repository at this point in the history
…in-sweep-reply

Add admin sweep reply
  • Loading branch information
ben-pr-p authored Jan 29, 2019
2 parents c206775 + 4bb7bff commit 5aa3047
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 34 deletions.
178 changes: 178 additions & 0 deletions src/components/IncomingMessageList/ConversationPreviewModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import gql from 'graphql-tag'
import { StyleSheet, css } from 'aphrodite'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'

import loadData from '../../containers//hoc/load-data'
import wrapMutations from '../../containers/hoc/wrap-mutations'
import MessageResponse from './MessageResponse';

const styles = StyleSheet.create({
conversationRow: {
color: 'white',
padding: '10px',
borderRadius: '5px',
fontWeight: 'normal',
}
})

class MessageList extends Component {
componentDidMount() {
this.refs.messageWindow.scrollTo(0, this.refs.messageWindow.scrollHeight)
}

componentDidUpdate() {
this.refs.messageWindow.scrollTo(0, this.refs.messageWindow.scrollHeight)
}

render() {
return (
<div ref="messageWindow" style={{maxHeight: '300px', overflowY: 'scroll'}}>
{this.props.messages.map((message, index) => {
const isFromContact = message.isFromContact
const messageStyle = {
marginLeft: isFromContact ? undefined : '60px',
marginRight: isFromContact ? '60px' : undefined,
backgroundColor: isFromContact ? '#AAAAAA' : 'rgb(33, 150, 243)',
}

return (
<p key={index} className={css(styles.conversationRow)} style={messageStyle}>
{message.text}
</p>
)
})}
</div>
)
}
}

MessageList.propTypes = {
messages: PropTypes.arrayOf(PropTypes.object),
}

class ConversationPreviewBody extends Component {
constructor(props) {
super(props)

this.state = {
messages: props.conversation.contact.messages
}

this.messagesChanged = this.messagesChanged.bind(this)
}

messagesChanged(messages) {
this.setState({ messages })
}

render() {
return (
<div>
<MessageList messages={this.state.messages} />
<MessageResponse conversation={this.props.conversation} messagesChanged={this.messagesChanged} />
</div>
)
}
}

ConversationPreviewBody.propTypes = {
conversation: PropTypes.object
}

class ConversationPreviewModal extends Component {
constructor(props) {
super(props)

this.state = {
optOutError: ''
}
}

handleClickOptOut = async () => {
const { contact } = this.props.conversation
const optOut = {
cell: contact.cell,
assignmentId: contact.assignmentId
}
try {
const response = await this.props.mutations.createOptOut(optOut, campaignContactId)
if (response.errors) {
const errorText = response.errors.join('\n')
throw new Error(errorText)
}
} catch (error) {
this.setState({ optOutError: error.message })
}
}

render() {
const { conversation } = this.props,
isOpen = conversation !== undefined

const primaryActions = [
<FlatButton
label="Opt-Out"
secondary={true}
onClick={this.handleClickOptOut}
/>,
<FlatButton
label="Close"
primary={true}
onClick={this.props.onRequestClose}
/>
]

return (
<Dialog
title='Messages'
open={isOpen}
actions={primaryActions}
modal={false}
onRequestClose={this.props.onRequestClose}
>
<div>
{isOpen && <ConversationPreviewBody {...this.props} />}
<Dialog
title='Error Opting Out'
open={!!this.state.optOutError}
modal={false}
>
<p>{this.state.optOutError}</p>
</Dialog>
</div>
</Dialog>
)
}
}

ConversationPreviewModal.propTypes = {
conversation: PropTypes.object,
onRequestClose: PropTypes.func
}

const mapMutationsToProps = () => ({
createOptOut: (optOut, campaignContactId) => ({
mutation: gql`
mutation createOptOut($optOut: OptOutInput!, $campaignContactId: String!) {
createOptOut(optOut: $optOut, campaignContactId: $campaignContactId) {
id
optOut {
id
createdAt
}
}
}
`,
variables: {
optOut,
campaignContactId
}
})
})

export default loadData(wrapMutations(ConversationPreviewModal), {
mapMutationsToProps
})
164 changes: 164 additions & 0 deletions src/components/IncomingMessageList/MessageResponse.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Form from 'react-formal'
import yup from 'yup'
import gql from 'graphql-tag'
import { StyleSheet, css } from 'aphrodite'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'

import loadData from '../../containers//hoc/load-data'
import wrapMutations from '../../containers/hoc/wrap-mutations'
import GSForm from '../../components/forms/GSForm'
import SendButton from '../../components/SendButton'

const styles = StyleSheet.create({
messageField: {
padding: '0px 8px',
},
})

class MessageResponse extends Component {
constructor(props) {
super(props)

this.state = {
messageText: '',
isSending: false,
sendError: ''
}

this.handleCloseErrorDialog = this.handleCloseErrorDialog.bind(this)
}

createMessageToContact(text) {
const { contact, texter } = this.props.conversation

return {
assignmentId: contact.assignmentId,
contactNumber: contact.cell,
userId: texter.id,
text
}
}

handleMessageFormChange = ({ messageText }) => this.setState({ messageText })

handleMessageFormSubmit = async ({ messageText }) => {
const { contact } = this.props.conversation
const message = this.createMessageToContact(messageText)
if (this.state.isSending) {
return // stops from multi-send
}
this.setState({ isSending: true })

const finalState = { isSending: false }
try {
const response = await this.props.mutations.sendMessage(message, contact.id)
const { messages } = response.data.sendMessage
this.props.messagesChanged(messages)
finalState.messageText = ''
} catch (e) {
finalState.sendError = e.message
}

this.setState(finalState)
}

handleCloseErrorDialog() {
this.setState({ sendError: '' })
}

handleClickSendMessageButton = () => {
this.refs.messageForm.submit()
}

render() {
const messageSchema = yup.object({
messageText: yup.string().required('Can\'t send empty message').max(window.MAX_MESSAGE_LENGTH)
})

const { messageText, isSending } = this.state
const isSendDisabled = isSending || messageText.trim() === ''

const errorActions = [
<FlatButton
label="OK"
primary={true}
onClick={this.handleCloseErrorDialog}
/>
]

return (
<div className={css(styles.messageField)}>
<GSForm
ref='messageForm'
schema={messageSchema}
value={{ messageText: this.state.messageText }}
onSubmit={this.handleMessageFormSubmit}
onChange={this.handleMessageFormChange}
>
<div style={{position: 'relative'}}>
<div style={{position: 'absolute', right: 0, bottom: 0, width: '120px'}}>
<SendButton
threeClickEnabled={false}
onFinalTouchTap={this.handleClickSendMessageButton}
disabled={isSendDisabled}
/>
</div>
<div style={{marginRight: '120px'}}>
<Form.Field
name='messageText'
label='Send a response'
multiLine
fullWidth
disabled={isSending}
rowsMax={6}
/>
</div>
</div>
</GSForm>
<Dialog
title='Error Sending'
open={!!this.state.sendError}
actions={errorActions}
modal={false}
>
<p>{this.state.sendError}</p>
</Dialog>
</div>
)
}
}

MessageResponse.propTypes = {
conversation: PropTypes.object,
messagesChanged: PropTypes.func
}

const mapMutationsToProps = () => ({
sendMessage: (message, campaignContactId) => ({
mutation: gql`
mutation sendMessage($message: MessageInput!, $campaignContactId: String!) {
sendMessage(message: $message, campaignContactId: $campaignContactId) {
id
messageStatus
messages {
id
createdAt
text
isFromContact
}
}
}
`,
variables: {
message,
campaignContactId
}
})
})

export default loadData(wrapMutations(MessageResponse), {
mapMutationsToProps
})
Loading

0 comments on commit 5aa3047

Please sign in to comment.