Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Client refactoring #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
alex35mil merged 12 commits into master from client-refactoring
Sep 27, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
12 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move js app to /client/app dir, update structure, fix linter and styl...
...istic issues
  • Loading branch information
alex35mil committed Sep 26, 2015
commit 537c985dc82faee333d80509343ca32a3965f9dd
4 changes: 2 additions & 2 deletions app/assets/javascripts/application.js
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//= require es5-shim/es5-shim

// It is important that generated/client-bundle must be before bootstrap since it is exposing jQuery and jQuery-ujs
//= require generated/vendor
//= require generated/app
//= require generated/vendor-bundle
//= require generated/app-bundle
//= require bootstrap-sprockets
//= require turbolinks
2 changes: 1 addition & 1 deletion client/.eslintrc
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ env:
rules:
indent: [1, 2, { SwitchCase: 1, VariableDeclarator: 2 }]
react/sort-comp: 0
react/jsx-quotes: 0
react/jsx-quotes: 1
4 changes: 3 additions & 1 deletion client/.jscsrc
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"preset": "airbnb",
"fileExtensions": [".js", ".jsx"],
"excludeFiles": ["build/**", "node_modules/**"]
"excludeFiles": ["build/**", "node_modules/**"],

"validateQuoteMarks": null // Issue with JSX quotemarks: https://github.com/jscs-dev/babel-jscs/issues/12
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import CommentsManager from '../utils/CommentsManager';
import * as actionTypes from '../constants/ActionTypes';
import commentsManager from '../utils/commentsManager';
import * as actionTypes from '../constants/commentsConstants';

export function incrementAjaxCounter() {
return {
Expand Down Expand Up @@ -44,25 +44,23 @@ export function submitCommentFailure(error) {
export function fetchComments() {
return dispatch => {
return (
CommentsManager.fetchComments()
commentsManager
.fetchComments()
.then(res => dispatch(fetchCommentsSuccess(res.data)))
.catch(res => dispatch(fetchCommentsFailure(res.data)))
);
};
}

function dispatchDecrementAjaxCounter(dispatch) {
return dispatch(decrementAjaxCounter());
}

export function submitComment(comment) {
return dispatch => {
dispatch(incrementAjaxCounter());
return (
CommentsManager.submitComment(comment)
commentsManager
.submitComment(comment)
.then(res => dispatch(submitCommentSuccess(res.data)))
.catch(res => dispatch(submitCommentFailure(res.data)))
.then(() => dispatchDecrementAjaxCounter(dispatch))
.then(() => dispatch(decrementAjaxCounter()))
);
};
}
26 changes: 26 additions & 0 deletions client/app/components/Comment.jsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { PropTypes } from 'react';
import marked from 'marked';

const Comment = React.createClass({
displayName: 'Comment',

propTypes: {
author: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
},

render() {
const { author, text } = this.props;
const rawMarkup = marked(text, { gfm: true, sanitize: true });
return (
<div className="comment">
<h2 className="comment-author">
{author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}}/>
</div>
);
},
});

export default Comment;
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import React from 'react';
import React, { PropTypes } from 'react';

import CommentForm from './CommentForm';
import CommentList from './CommentList';

const CommentBox = React.createClass({
displayName: 'CommentBox',

propTypes: {
pollInterval: React.PropTypes.number.isRequired,
actions: React.PropTypes.object.isRequired,
data: React.PropTypes.object.isRequired,
pollInterval: PropTypes.number.isRequired,
actions: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
},

componentDidMount() {
const { fetchComments } = this.props.actions;
fetchComments();
setInterval(fetchComments,
this.props.pollInterval);
setInterval(fetchComments, this.props.pollInterval);
},

ajaxCounter() {
Expand All @@ -30,20 +30,23 @@ const CommentBox = React.createClass({
const { actions, data } = this.props;

return (
<div className='commentBox container'>
<div className="commentBox container">
<h1>
Comments { this.isSendingAjax() ? `SENDING AJAX REQUEST! Ajax Counter is ${this.ajaxCounter()}` : '' }
Comments { this.isSendingAjax() && `SENDING AJAX REQUEST! Ajax Counter is ${this.ajaxCounter()}` }
</h1>
<p>
Text take Github Flavored Markdown. Comments older than 24 hours are deleted.
<b>Name</b> is preserved, <b>Text</b> is reset, between submits.</p>
<b>Name</b> is preserved, <b>Text</b> is reset, between submits.
</p>
<CommentForm
ajaxSending={this.isSendingAjax()}
error={data.get('submitCommentError')}
actions={actions}/>
actions={actions}
/>
<CommentList
comments={data.get('comments')}
error={data.get('fetchCommentError')}/>
$$comments={data.get('$$comments')}
error={data.get('fetchCommentError')}
/>
</div>
);
},
Expand Down
244 changes: 244 additions & 0 deletions client/app/components/CommentForm.jsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import React, { PropTypes } from 'react';
import Input from 'react-bootstrap/lib/Input';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Nav from 'react-bootstrap/lib/Nav';
import NavItem from 'react-bootstrap/lib/NavItem';
import Alert from 'react-bootstrap/lib/Alert';
import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup';

const emptyComment = { author: '', text: '' };
const textPlaceholder = 'Say something using markdown...';

const CommentForm = React.createClass({
displayName: 'CommentForm',

propTypes: {
ajaxSending: PropTypes.bool.isRequired,
actions: PropTypes.object.isRequired,
error: PropTypes.any,
},

getInitialState() {
return {
formMode: 0,
comment: emptyComment,
};
},

handleSelect(selectedKey) {
this.setState({ formMode: selectedKey });
},

handleChange() {
let comment;

// This could also be done using ReactLink:
// http://facebook.github.io/react/docs/two-way-binding-helpers.html
if (this.state.formMode < 2) {
comment = {
author: this.refs.author.getValue(),
text: this.refs.text.getValue(),
};
} else {
comment = {
// This is different because the input is a native HTML element
// rather than a React element.
author: this.refs.inlineAuthor.getDOMNode().value,
text: this.refs.inlineText.getDOMNode().value,
};
}

this.setState({ comment });
},

handleSubmit(e) {
e.preventDefault();
const { actions } = this.props;
actions
.submitComment(this.state.comment)
.then(this.resetAndFocus);
},

resetAndFocus() {
// Don't reset a form that didn't submit, this results in data loss
if (this.props.error) return;

const comment = { author: this.state.comment.author, text: '' };
this.setState({ comment });

let ref;
if (this.state.formMode < 2) {
ref = this.refs.text.getInputDOMNode();
} else {
ref = React.findDOMNode(this.refs.inlineText);
}

ref.focus();
},

formHorizontal() {
return (
<div>
<hr />
<form className="commentForm form-horizontal" onSubmit={this.handleSubmit}>
<Input
type="text"
label="Name"
placeholder="Your Name"
labelClassName="col-sm-2"
wrapperClassName="col-sm-10"
ref="author"
value={this.state.comment.author}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
<Input
type="textarea"
label="Text"
placeholder={textPlaceholder}
labelClassName="col-sm-2"
wrapperClassName="col-sm-10"
ref="text"
value={this.state.comment.text}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<input
type="submit"
className="btn btn-primary"
value="Post"
disabled={this.props.ajaxSending}
/>
</div>
</div>
</form>
</div>
);
},

formStacked() {
return (
<div>
<hr />
<form className="commentForm form" onSubmit={this.handleSubmit}>
<Input
type="text"
label="Name"
placeholder="Your Name"
ref="author"
value={this.state.comment.author}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
<Input
type="textarea"
label="Text"
placeholder={textPlaceholder}
ref="text"
value={this.state.comment.text}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
<input
type="submit"
className="btn btn-primary"
value="Post"
disabled={this.props.ajaxSending}
/>
</form>
</div>
);
},

formInline() {
return (
<div>
<hr />
<form className="commentForm form" onSubmit={this.handleSubmit}>
<Input label="Inline Form" wrapperClassName="wrapper">
<Row>
<Col xs={3}>
<input
type="text"
className="form-control"
placeholder="Your Name"
ref="inlineAuthor"
value={this.state.comment.author}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
</Col>
<Col xs={8}>
<input
type="text"
className="form-control"
placeholder={textPlaceholder}
ref="inlineText"
value={this.state.comment.text}
onChange={this.handleChange}
disabled={this.props.ajaxSending}
/>
</Col>
<Col xs={1}>
<input
type="submit"
className="btn btn-primary"
value="Post"
disabled={this.props.ajaxSending}
/>
</Col>
</Row>
</Input>
</form>
</div>
);
},

errorWarning() {
// If there is no error, there is nothing to add to the DOM
if (!this.props.error) return undefined;
return (
<Alert bsStyle="danger" key="commentSubmissionError">
<strong>Your comment was not saved!</strong>
A server error prevented your comment from being saved. Please try again.
</Alert>
);
},

render() {
let inputForm;
switch (this.state.formMode) {
case 0:
inputForm = this.formHorizontal();
break;
case 1:
inputForm = this.formStacked();
break;
case 2:
inputForm = this.formInline();
break;
default:
throw new Error(`Unknown form mode: ${this.state.formMode}.`);
}
return (
<div>

<ReactCSSTransitionGroup transitionName="element">
{this.errorWarning()}
</ReactCSSTransitionGroup>

<Nav bsStyle="pills" activeKey={this.state.formMode} onSelect={this.handleSelect}>
<NavItem eventKey={0}>Horizontal Form</NavItem>
<NavItem eventKey={1}>Stacked Form</NavItem>
<NavItem eventKey={2}>Inline Form</NavItem>
</Nav>
{inputForm}
</div>
);
},
});

export default CommentForm;
Loading

AltStyle によって変換されたページ (->オリジナル) /