Skip to content

Instantly share code, notes, and snippets.

@captainill
Forked from gaearon/feedback_widget.js
Created May 26, 2014 08:29
Show Gist options
  • Save captainill/5b5167a69afe7698f06a to your computer and use it in GitHub Desktop.
Save captainill/5b5167a69afe7698f06a to your computer and use it in GitHub Desktop.
/** @jsx React.DOM */
/* jshint trailing:false, quotmark:false, newcap:false */
define(function (require) {
'use strict';
var React = require('react'),
Promise = require('bluebird'),
$ = require('jquery');
var UNSENT_FEEDBACK_KEY = 'common.views.FeedbackWidget.unsentFeedback';
var STATUS = {
INITIAL: 'initial',
SENDING: 'sending',
SENT: 'sent'
};
var FeedbackWidget = React.createClass({
propTypes: {
closeModal: React.PropTypes.func.isRequired
},
getInitialState: function () {
return {
text: this.getFeedbackFromLocalStorage() || '',
status: STATUS.INITIAL
};
},
render: function () {
var status = this.state.status,
footer;
footer = (status === STATUS.SENT) ?
<span>Thank you for your feedback.</span> :
<a className='Btn Btn--large' onClick={this.beginSend}
data-disabled={status !== STATUS.INITIAL}>
Send
</a>;
return (
<div className='Feedback'>
<div onClick={this.props.showModal} className='Modal-close'>
<i className='icon-cross'></i>
</div>
<h3 className='Modal-title'>
Send Feedback
</h3>
<p className='Modal-message'>
Got ideas, problems, or just want to say hi?
</p>
<div className='Form'>
<div className='Form-group'>
<textarea className='Form-control Feedback-field'
placeholder='Jot it down here and press Send'
value={this.state.text}
onChange={this.handleChange}
ref='textarea'
autoFocus
disabled={status !== STATUS.INITIAL} />
</div>
</div>
<div className='Modal-footer'>
{footer}
</div>
</div>
);
},
handleChange: function (e) {
var text = e.target.value;
if (this.state.status === STATUS.INITIAL) {
this.saveFeedbackInLocalStorage(text);
}
this.setState({
text: text
});
},
setStatus: function (status) {
if (!status) {
throw new Error('Empty status');
}
this.setState({
status: status
});
},
beginSend: function () {
if (this.state.status !== STATUS.INITIAL) {
throw new Error('Gotta be in initial state.');
}
this.setStatus(STATUS.SENDING);
this.postFeedback(this.state.text)
.bind(this)
.then(function () {
this.setStatus(STATUS.SENT);
this.clearFeedbackInLocalStorage();
})
.catch(function (err) {
this.setStatus(STATUS.INITIAL);
this.refs.textarea.getDOMNode().focus();
console.log('Error sending feedback', err);
});
},
postFeedback: function (text) {
var data = {
'feedback': text,
'current_url': window.location.href
};
return Promise.cast($.ajax({
url: '/api/feedback',
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}));
},
getFeedbackFromLocalStorage: function () {
return localStorage.getItem(UNSENT_FEEDBACK_KEY);
},
saveFeedbackInLocalStorage: function (text) {
localStorage.setItem(UNSENT_FEEDBACK_KEY, text);
},
clearFeedbackInLocalStorage: function () {
localStorage.removeItem(UNSENT_FEEDBACK_KEY);
}
});
return FeedbackWidget;
});
/** @jsx React.DOM */
/* jshint trailing:false, quotmark:false, newcap:false */
define(function (require) {
'use strict';
var React = require('react'),
Promise = require('bluebird'),
$ = require('jquery'),
_ = require('underscore');
var STATUS = {
INITIAL: 'initial',
SENDING: 'sending',
SENT: 'sent'
};
var JoinWidget = React.createClass({
mixins: [React.addons.LinkedStateMixin],
propTypes: {
closeModal: React.PropTypes.func.isRequired,
showLogin: React.PropTypes.func.isRequired
},
getInitialState: function () {
return {
name: '',
email: '',
website: '',
status: STATUS.INITIAL
};
},
maySend: function () {
return this.state.status === STATUS.INITIAL &&
this.state.name &&
this.state.email &&
this.state.website;
},
getFooter: function (status) {
if (status === STATUS.SENT) {
return (
<div className='Modal-footer'>
Thank you! We will contact you soon.
</div>
);
}
return (
<div className='Modal-footer'>
<span className='Modal-footer-text'>
Already invited?
<a onClick={this.props.showLogin}>Sign in</a>.
</span>
<a className='Btn Btn--large' onClick={this.beginSend}
data-disabled={!this.maySend()}>
Send
</a>
</div>
);
},
render: function () {
var status = this.state.status,
footer = this.getFooter(status);
return (
<div className='social-Join'>
<div className='Modal-close' onClick={this.props.closeModal}>
<i className='icon-cross'></i>
</div>
<h3 className='Modal-title'>
Request Invite
</h3>
<p className='Modal-message'>
Want to be a part of (redacted)? Awesome!
<br/><br/>
For now we’re invite-only. Fill in the application and hit Send.
</p>
<div className='Form'>
<div className='Form-group'>
<input valueLink={this.linkState('name')} disabled={status !== STATUS.INITIAL} autofocus
type='text' className='Form-control' placeholder='Name' />
<input valueLink={this.linkState('email')} disabled={status !== STATUS.INITIAL}
type='email' className='Form-control' placeholder='Email' />
<input valueLink={this.linkState('website')} disabled={status !== STATUS.INITIAL}
type='url' className='Form-control' placeholder='Website' />
</div>
</div>
{footer}
</div>
);
},
setStatus: function (status) {
if (!status) {
throw new Error('Empty status');
}
this.setState({
status: status
});
},
beginSend: function () {
if (this.state.status !== STATUS.INITIAL) {
throw new Error('Gotta be in initial state.');
}
this.setStatus(STATUS.SENDING);
this.sendInviteRequest(_.pick(this.state, 'email', 'name', 'website'))
.bind(this)
.then(function () {
this.setStatus(STATUS.SENT);
})
.catch(function (err) {
this.setStatus(STATUS.INITIAL);
console.log('Error requesting invite', err);
});
},
sendInviteRequest: function (options) {
var data = _.extend(options, {
'current_url': window.location.href
});
return Promise.cast($.ajax({
url: '/api/invite_requests',
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}));
}
});
return JoinWidget;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment