commit: d0e2733f63a7bd9601e73adee1107da804f85c41
parent: f24cb32e99afdb9fa31067fb647912fec92e9ed4
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 3 Sep 2016 14:01:10 +0200
Fix reblogs of reblogs in UI, add follow form in UI
Diffstat:
12 files changed, 193 insertions(+), 101 deletions(-)
diff --git a/app/assets/javascripts/components/actions/follow.jsx b/app/assets/javascripts/components/actions/follow.jsx
@@ -6,41 +6,41 @@ export const FOLLOW_SUBMIT_REQUEST = 'FOLLOW_SUBMIT_REQUEST';
export const FOLLOW_SUBMIT_SUCCESS = 'FOLLOW_SUBMIT_SUCCESS';
export const FOLLOW_SUBMIT_FAIL = 'FOLLOW_SUBMIT_FAIL';
-export function followChange(text) {
+export function changeFollow(text) {
return {
type: FOLLOW_CHANGE,
text: text
};
}
-export function followSubmit() {
+export function submitFollow() {
return function (dispatch, getState) {
- dispatch(followSubmitRequest());
+ dispatch(submitFollowRequest());
api(getState).post('/api/follows', {
uri: getState().getIn(['follow', 'text'])
}).then(function (response) {
- dispatch(followSubmitSuccess(response.data));
+ dispatch(submitFollowSuccess(response.data));
}).catch(function (error) {
- dispatch(followSubmitFail(error));
+ dispatch(submitFollowFail(error));
});
};
}
-export function followSubmitRequest() {
+export function submitFollowRequest() {
return {
type: FOLLOW_SUBMIT_REQUEST
};
}
-export function followSubmitSuccess(account) {
+export function submitFollowSuccess(account) {
return {
type: FOLLOW_SUBMIT_SUCCESS,
account: account
};
}
-export function followSubmitFail(error) {
+export function submitFollowFail(error) {
return {
type: FOLLOW_SUBMIT_FAIL,
error: error
diff --git a/app/assets/javascripts/components/components/compose_form.jsx b/app/assets/javascripts/components/components/compose_form.jsx
@@ -0,0 +1,57 @@
+import CharacterCounter from './character_counter';
+import Button from './button';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ReplyIndicator from './reply_indicator';
+
+const ComposeForm = React.createClass({
+
+ propTypes: {
+ text: React.PropTypes.string.isRequired,
+ is_submitting: React.PropTypes.bool,
+ in_reply_to: ImmutablePropTypes.map,
+ onChange: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancelReply: React.PropTypes.func.isRequired
+ },
+
+ mixins: [PureRenderMixin],
+
+ handleChange (e) {
+ this.props.onChange(e.target.value);
+ },
+
+ handleKeyUp (e) {
+ if (e.keyCode === 13 && e.ctrlKey) {
+ this.props.onSubmit();
+ }
+ },
+
+ handleSubmit () {
+ this.props.onSubmit();
+ },
+
+ render () {
+ let replyArea = '';
+
+ if (this.props.in_reply_to) {
+ replyArea = <ReplyIndicator status={this.props.in_reply_to} onCancel={this.props.onCancelReply} />;
+ }
+
+ return (
+ <div style={{ marginBottom: '30px', padding: '10px' }}>
+ {replyArea}
+
+ <textarea disabled={this.props.is_submitting} placeholder='What is on your mind?' value={this.props.text} onKeyUp={this.handleKeyUp} onChange={this.handleChange} className='compose-form__textarea' style={{ display: 'block', boxSizing: 'border-box', width: '100%', height: '100px', resize: 'none', border: 'none', color: '#282c37', padding: '10px', fontFamily: 'Roboto', fontSize: '14px', margin: '0' }} />
+
+ <div style={{ marginTop: '10px', overflow: 'hidden' }}>
+ <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={this.props.is_submitting} /></div>
+ <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter text={this.props.text} /></div>
+ </div>
+ </div>
+ );
+ }
+
+});
+
+export default ComposeForm;
diff --git a/app/assets/javascripts/components/components/composer_drawer.jsx b/app/assets/javascripts/components/components/composer_drawer.jsx
@@ -1,57 +0,0 @@
-import CharacterCounter from './character_counter';
-import Button from './button';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ReplyIndicator from './reply_indicator';
-
-const ComposerDrawer = React.createClass({
-
- propTypes: {
- text: React.PropTypes.string.isRequired,
- is_submitting: React.PropTypes.bool,
- in_reply_to: ImmutablePropTypes.map,
- onChange: React.PropTypes.func.isRequired,
- onSubmit: React.PropTypes.func.isRequired,
- onCancelReply: React.PropTypes.func.isRequired
- },
-
- mixins: [PureRenderMixin],
-
- handleChange (e) {
- this.props.onChange(e.target.value);
- },
-
- handleKeyUp (e) {
- if (e.keyCode === 13 && e.ctrlKey) {
- this.props.onSubmit();
- }
- },
-
- handleSubmit () {
- this.props.onSubmit();
- },
-
- render () {
- let replyArea = '';
-
- if (this.props.in_reply_to) {
- replyArea = <ReplyIndicator status={this.props.in_reply_to} onCancel={this.props.onCancelReply} />;
- }
-
- return (
- <div style={{ width: '280px', boxSizing: 'border-box', background: '#454b5e', margin: '10px', marginRight: '0', padding: '10px' }}>
- {replyArea}
-
- <textarea disabled={this.props.is_submitting} placeholder='What is on your mind?' value={this.props.text} onKeyUp={this.handleKeyUp} onChange={this.handleChange} className='compose-drawer__textarea' style={{ display: 'block', boxSizing: 'border-box', width: '100%', height: '100px', resize: 'none', border: 'none', color: '#282c37', padding: '10px', fontFamily: 'Roboto', fontSize: '14px', margin: '0' }} />
-
- <div style={{ marginTop: '10px', overflow: 'hidden' }}>
- <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={this.props.is_submitting} /></div>
- <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter text={this.props.text} /></div>
- </div>
- </div>
- );
- }
-
-});
-
-export default ComposerDrawer;
diff --git a/app/assets/javascripts/components/components/drawer.jsx b/app/assets/javascripts/components/components/drawer.jsx
@@ -0,0 +1,17 @@
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+
+const Drawer = React.createClass({
+
+ mixins: [PureRenderMixin],
+
+ render () {
+ return (
+ <div style={{ width: '280px', boxSizing: 'border-box', background: '#454b5e', margin: '10px', marginRight: '0', padding: '0', display: 'flex', flexDirection: 'column' }}>
+ {this.props.children}
+ </div>
+ );
+ }
+
+});
+
+export default Drawer;
diff --git a/app/assets/javascripts/components/components/follow_form.jsx b/app/assets/javascripts/components/components/follow_form.jsx
@@ -0,0 +1,40 @@
+import IconButton from './icon_button';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+
+const FollowForm = React.createClass({
+
+ propTypes: {
+ text: React.PropTypes.string.isRequired,
+ is_submitting: React.PropTypes.bool,
+ onChange: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired
+ },
+
+ mixins: [PureRenderMixin],
+
+ handleChange (e) {
+ this.props.onChange(e.target.value);
+ },
+
+ handleKeyUp (e) {
+ if (e.keyCode === 13) {
+ this.props.onSubmit();
+ }
+ },
+
+ handleSubmit () {
+ this.props.onSubmit();
+ },
+
+ render () {
+ return (
+ <div style={{ display: 'flex', lineHeight: '20px', padding: '10px', background: '#373b4a' }}>
+ <input type='text' disabled={this.props.is_submitting} placeholder='username@domain' value={this.props.text} onKeyUp={this.handleKeyUp} onChange={this.handleChange} className='follow-form__input' style={{ flex: '1 1 auto', boxSizing: 'border-box', display: 'block', border: 'none', padding: '10px', fontFamily: 'Roboto', color: '#282c37', fontSize: '14px', margin: '0' }} />
+ <div style={{ padding: '10px', paddingRight: '0' }}><IconButton title='Follow' size={20} icon='user-plus' onClick={this.handleSubmit} disabled={this.props.is_submitting} /></div>
+ </div>
+ );
+ }
+
+});
+
+export default FollowForm;
diff --git a/app/assets/javascripts/components/components/frontend.jsx b/app/assets/javascripts/components/components/frontend.jsx
@@ -1,6 +1,8 @@
-import ColumnsArea from './columns_area';
-import ComposerDrawerContainer from '../containers/composer_drawer_container';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import ColumnsArea from './columns_area';
+import Drawer from './drawer';
+import ComposeFormContainer from '../containers/compose_form_container';
+import FollowFormContainer from '../containers/follow_form_container';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
const Frontend = React.createClass({
@@ -9,7 +11,14 @@ const Frontend = React.createClass({
render () {
return (
<div style={{ flex: '0 0 auto', display: 'flex', width: '100%', height: '100%', background: '#1a1c23' }}>
- <ComposerDrawerContainer />
+ <Drawer>
+ <div style={{ flex: '1 1 auto' }}>
+ <ComposeFormContainer />
+ </div>
+
+ <FollowFormContainer />
+ </Drawer>
+
<ColumnsArea />
</div>
);
diff --git a/app/assets/javascripts/components/containers/compose_form_container.jsx b/app/assets/javascripts/components/containers/compose_form_container.jsx
@@ -0,0 +1,29 @@
+import { connect } from 'react-redux';
+import ComposeForm from '../components/compose_form';
+import { changeCompose, submitCompose, cancelReplyCompose } from '../actions/compose';
+
+const mapStateToProps = function (state, props) {
+ return {
+ text: state.getIn(['compose', 'text']),
+ is_submitting: state.getIn(['compose', 'is_submitting']),
+ in_reply_to: state.getIn(['compose', 'in_reply_to'])
+ };
+};
+
+const mapDispatchToProps = function (dispatch) {
+ return {
+ onChange: function (text) {
+ dispatch(changeCompose(text));
+ },
+
+ onSubmit: function () {
+ dispatch(submitCompose());
+ },
+
+ onCancelReply: function () {
+ dispatch(cancelReplyCompose());
+ }
+ }
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
diff --git a/app/assets/javascripts/components/containers/composer_drawer_container.jsx b/app/assets/javascripts/components/containers/composer_drawer_container.jsx
@@ -1,29 +0,0 @@
-import { connect } from 'react-redux';
-import ComposerDrawer from '../components/composer_drawer';
-import { changeCompose, submitCompose, cancelReplyCompose } from '../actions/compose';
-
-const mapStateToProps = function (state, props) {
- return {
- text: state.getIn(['compose', 'text']),
- is_submitting: state.getIn(['compose', 'is_submitting']),
- in_reply_to: state.getIn(['compose', 'in_reply_to'])
- };
-};
-
-const mapDispatchToProps = function (dispatch) {
- return {
- onChange: function (text) {
- dispatch(changeCompose(text));
- },
-
- onSubmit: function () {
- dispatch(submitCompose());
- },
-
- onCancelReply: function () {
- dispatch(cancelReplyCompose());
- }
- }
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComposerDrawer);
diff --git a/app/assets/javascripts/components/containers/follow_form_container.jsx b/app/assets/javascripts/components/containers/follow_form_container.jsx
@@ -0,0 +1,24 @@
+import { connect } from 'react-redux';
+import FollowForm from '../components/follow_form';
+import { changeFollow, submitFollow } from '../actions/follow';
+
+const mapStateToProps = function (state, props) {
+ return {
+ text: state.getIn(['follow', 'text']),
+ is_submitting: state.getIn(['follow', 'is_submitting'])
+ };
+};
+
+const mapDispatchToProps = function (dispatch) {
+ return {
+ onChange: function (text) {
+ dispatch(changeFollow(text));
+ },
+
+ onSubmit: function () {
+ dispatch(submitFollow());
+ }
+ }
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(FollowForm);
diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx
@@ -9,6 +9,8 @@ function updateMatchingStatuses(state, needle, callback) {
return list.map(function (status) {
if (status.get('id') === needle.get('id')) {
return callback(status);
+ } else if (status.getIn(['reblog', 'id'], null) === needle.get('id')) {
+ return status.set('reblog', callback(status.get('reblog')));
}
return status;
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
@@ -28,7 +28,7 @@
}
}
-.compose-drawer__textarea {
+.compose-form__textarea, .follow-form__input {
background: #fff;
&:disabled {
diff --git a/app/models/account.rb b/app/models/account.rb
@@ -59,11 +59,11 @@ class Account < ApplicationRecord
end
def favourited?(status)
- (status.reblog? ? status.reblog : status).favourites.where(account: self).count == 1
+ (status.reblog? ? status.reblog : status).favourites.where(account: self).count > 0
end
def reblogged?(status)
- (status.reblog? ? status.reblog : status).reblogs.where(account: self).count == 1
+ (status.reblog? ? status.reblog : status).reblogs.where(account: self).count > 0
end
def keypair