Skip to content

Commit

Permalink
Decaffeinate CommentBox + improve Talk error messages (#7161)
Browse files Browse the repository at this point in the history
* CommentBox: prepare error messages

* Decaffeinate CommentBox

* CommentBox: implement getErrorMessage() to add additional info to server error messages

* Fix: CoffeeScript Talk components need require.default for CommentBox
  • Loading branch information
shaunanoordin authored Sep 12, 2024
1 parent 48910b3 commit 55228b2
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 158 deletions.
151 changes: 0 additions & 151 deletions app/talk/comment-box.cjsx

This file was deleted.

204 changes: 204 additions & 0 deletions app/talk/comment-box.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import ToggleChildren from './mixins/toggle-children';
import Feedback from './mixins/feedback';
import CommentImageSelector from './comment-image-selector';
import getSubjectLocation from '../lib/getSubjectLocation';
import Loading from '../components/loading-indicator';
import SingleSubmitButton from '../components/single-submit-button';
import alert from '../lib/alert';
import {Markdown, MarkdownEditor, MarkdownHelp} from 'markdownz';
import Suggester from './suggester';

const ERROR_MESSAGES = {
BANNED: 'You are banned. Email [email protected] for further information.',
EMAIL_NOT_CONFIRMED: 'Your account email address is not yet confirmed. Please check your email for confirmation instructions, or visit https://www.zooniverse.org/settings/email to request new confirmation email.'
};

function getErrorMessage(error, user) {
if (!error) return '';

if (
error.message?.match?.(/not allowed to create this Comment/i)
|| error.message?.match?.(/not allowed to create this Discussion/i)
|| error.message?.match?.(/You must confirm your account/i)
) {
if (!user?.confirmed_at) {
return ERROR_MESSAGES.EMAIL_NOT_CONFIRMED;
}
} else if (
error.message?.match?.(/You are banned/i)
) {
return ERROR_MESSAGES.BANNED;
}

return error.message || 'Unknown error';
}

const CommentBox = createReactClass({
displayName: 'Commentbox',
mixins: [ToggleChildren, Feedback],

propTypes: {
submit: PropTypes.string,
user: PropTypes.object,
header: PropTypes.string,
placeholder: PropTypes.string,
submitFeedback: PropTypes.string,
onSubmitComment: PropTypes.func, // called on submit and passed (e, textarea-content, subject), expected to return something thenable
onCancelClick: PropTypes.func
}, // adds cancel button and calls callback on click if supplied

getDefaultProps() {
return {
submit: "Submit",
header: "Add to the discussion +",
placeholder: "Type your comment here",
submitFeedback: "Comment Successfully Submitted",
content: '',
subject: null,
user: null,
logSubmit: false
};
}, // if true, geordi logging will be made through onSubmitComment

getInitialState() {
return {
subject: this.props.subject,
content: this.props.content,
reply: null,
loading: false,
error: ''
};
},

contextTypes: {
geordi: PropTypes.object
},

logPageClick(clicked, button) {
return this.context?.geordi?.logEvent({
type: clicked,
data: button
});
},

componentWillReceiveProps(nextProps) {
if (nextProps.reply !== this.props.reply) {
return this.setState({reply: nextProps.reply});
}
},

onSubmitComment(e) {
if (this.props.logSubmit === true) {
this.logPageClick('add-comment', this.props.submit);
}
e.preventDefault();
const textareaValue = this.state.content;
if (this.props.validationCheck?.(textareaValue)) { return; }
this.setState({loading: true});

return this.props.onSubmitComment?.(e, textareaValue, this.state.subject, this.state.reply)
.then(() => {
this.hideChildren();
this.setState({subject: null, content: '', error: '', loading: false});
return this.setFeedback(this.props.submitFeedback);
}).catch(e => {
const errorMessage = getErrorMessage(e, this.props.user);
return this.setState({error: errorMessage, loading: false});
});
},

onInputChange(e) {
return this.setState({content: e.target.value});
},

onImageSelectClick(e) {
return this.toggleComponent('image-selector');
},

onSelectImage(imageData) {
this.setState({subject: imageData});
return this.hideChildren();
},

onClearImageClick(e) {
return this.setState({subject: null});
},

render() {
const validationErrors = this.props.validationErrors.map((message, i) => {
return <p key={i} className="talk-validation-error">{message}</p>;
});

const feedback = this.renderFeedback();
const loader = this.state.loading ? <Loading /> : undefined;

return (
<div className="talk-comment-container">
<div className="talk-comment-box">
{this.props.reply ?
<div className="talk-comment-reply">
<div style={{color: '#afaeae'}}>
In reply to {this.props.reply.comment.user_display_name}'s comment:
</div>
<Markdown project={this.props.project}>{this.props.reply.comment.body}</Markdown>
<button type="button" onClick={this.props.onClickClearReply}><i className="fa fa-close" /> Clear Reply</button>
</div> : undefined
}

<h1>{this.props.header}</h1>

{this.state.subject ?
<img className="talk-comment-focus-image" src={getSubjectLocation(this.state.subject).src} /> : undefined}

<form className="talk-comment-form" onSubmit={this.onSubmitComment}>
<Suggester {...this.props} input={this.state.content} onSelect={this.onInputChange}>
<MarkdownEditor previewing={this.state.loading} placeholder={this.props.placeholder} project={this.props.project} className="full" value={this.state.content} onChange={this.onInputChange} onHelp={() => alert(<MarkdownHelp talk={true} title={<h1>Guide to commenting in Talk</h1>}/>) }/>
</Suggester>
<section>
<button
type="button"
className={`talk-comment-image-select-button ${this.state.showing === 'image-selector' ? 'active' : ''}`}
onClick={this.onImageSelectClick}>
Linked Image
{this.state.showing === 'image-selector' ? <span>&nbsp;<i className="fa fa-close" /></span> : undefined}
</button>

<SingleSubmitButton type="submit" onClick={this.onSubmitComment} className='talk-comment-submit-button'>{this.props.submit}</SingleSubmitButton>
{this.props.onCancelClick ?
<button
type="button"
className="button talk-comment-submit-button"
onClick={this.props.onCancelClick}>
Cancel
</button> : undefined}
</section>

{feedback}

<div className="submit-error">
{validationErrors}
{this.state.error != null ? this.state.error : null}
</div>
</form>

<div className="talk-comment-children">
{(() => { switch (this.state.showing) {
case 'image-selector':
return <CommentImageSelector
onSelectImage={this.onSelectImage}
onClearImageClick={this.onClearImageClick}
user={this.props.user} />;
} })()}
</div>

{loader}
</div>
</div>
);
}
});

export default CommentBox;
2 changes: 1 addition & 1 deletion app/talk/comment.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ToggleChildren = require './mixins/toggle-children'
commentValidations = require './lib/comment-validations'
{getErrors} = require './lib/validations'
Feedback = require './mixins/feedback'
CommentBox = require './comment-box'
CommentBox = require('./comment-box').default
CommentReportForm = require './comment-report-form'
CommentLink = require './comment-link'
upvotedByCurrentUser = require './lib/upvoted-by-current-user'
Expand Down
2 changes: 1 addition & 1 deletion app/talk/discussion-new-form.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ React = require 'react'
PropTypes = require 'prop-types'
createReactClass = require 'create-react-class'
ReactDOM = require 'react-dom'
CommentBox = require './comment-box'
CommentBox = require('./comment-box').default
{getErrors} = require './lib/validations'
commentValidations = require './lib/comment-validations'
discussionValidations = require './lib/discussion-validations'
Expand Down
2 changes: 1 addition & 1 deletion app/talk/discussion.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ PropTypes = require 'prop-types'
createReactClass = require 'create-react-class'
ReactDOM = require 'react-dom'
Comment = require './comment'
CommentBox = require './comment-box'
CommentBox = require('./comment-box').default
talkClient = require 'panoptes-client/lib/talk-client'
apiClient = require 'panoptes-client/lib/api-client'
Paginator = require './lib/paginator'
Expand Down
2 changes: 1 addition & 1 deletion app/talk/inbox-conversation.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ apiClient = require 'panoptes-client/lib/api-client'
SingleSubmitButton = require '../components/single-submit-button'
HandlePropChanges = require '../lib/handle-prop-changes'
{Markdown} = require 'markdownz'
CommentBox = require './comment-box'
CommentBox = require('./comment-box').default
{Link} = require 'react-router'
{timestamp} = require './lib/time'
{ Helmet } = require 'react-helmet'
Expand Down
Loading

0 comments on commit 55228b2

Please sign in to comment.