Skip to content
Snippets Groups Projects
Commit 2e7aac79 authored by Eugen Rochko's avatar Eugen Rochko
Browse files

Adding sense of self to the UI, cleaning up routing, adding third (detail) column

parent d6a64f45
No related branches found
No related tags found
No related merge requests found
Showing
with 160 additions and 49 deletions
import api from '../api'
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
export const ACCOUNT_FETCH = 'ACCOUNT_FETCH';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
export function setAccountSelf(account) {
return {
type: ACCOUNT_SET_SELF,
account: account
};
};
export function fetchAccount(id) {
return (dispatch, getState) => {
dispatch(fetchAccountRequest(id));
api(getState).get(`/api/accounts/${id}`).then(response => {
dispatch(fetchAccountSuccess(response.data));
}).catch(error => {
dispatch(fetchAccountFail(id, error));
});
};
};
export function fetchAccountRequest(id) {
return {
type: ACCOUNT_FETCH_REQUEST,
id: id
};
};
export function fetchAccountSuccess(account) {
return {
type: ACCOUNT_FETCH_SUCCESS,
account: account
};
};
export function fetchAccountFail(id, error) {
return {
type: ACCOUNT_FETCH_FAIL,
id: id,
error: error
};
};
import api from '../api';
export const STATUS_FETCH = 'STATUS_FETCH';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL';
......@@ -5,7 +5,8 @@ const Column = React.createClass({
propTypes: {
heading: React.PropTypes.string,
icon: React.PropTypes.string
icon: React.PropTypes.string,
fluid: React.PropTypes.bool
},
mixins: [PureRenderMixin],
......@@ -22,8 +23,16 @@ const Column = React.createClass({
header = <ColumnHeader icon={this.props.icon} type={this.props.heading} onClick={this.handleHeaderClick} />;
}
const style = { width: '350px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', display: 'flex', flexDirection: 'column' };
if (this.props.fluid) {
style.width = 'auto';
style.flex = '1 1 auto';
style.background = '#21242d';
}
return (
<div style={{ width: '380px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', display: 'flex', flexDirection: 'column' }}>
<div style={style}>
{header}
{this.props.children}
</div>
......
......@@ -6,7 +6,7 @@ const ColumnsArea = React.createClass({
render () {
return (
<div style={{ display: 'flex', flexDirection: 'row', flex: '1' }}>
<div style={{ display: 'flex', flexDirection: 'row', flex: '1', marginRight: '10px' }}>
{this.props.children}
</div>
);
......
import ImmutablePropTypes from 'react-immutable-proptypes';
import PureRenderMixin from 'react-addons-pure-render-mixin';
const DisplayName = React.createClass({
......@@ -6,6 +7,8 @@ const DisplayName = React.createClass({
account: ImmutablePropTypes.map.isRequired
},
mixins: [PureRenderMixin],
render () {
let displayName = this.props.account.get('display_name');
......
import ColumnsArea from './columns_area';
import Column from './column';
import Drawer from './drawer';
import ColumnsArea from './columns_area';
import Column from './column';
import Drawer from './drawer';
import ComposeFormContainer from '../containers/compose_form_container';
import FollowFormContainer from '../containers/follow_form_container';
import UploadFormContainer from '../containers/upload_form_container';
import StatusListContainer from '../containers/status_list_container';
import NotificationsContainer from '../containers/notifications_container';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import NavigationContainer from '../containers/navigation_container';
import PureRenderMixin from 'react-addons-pure-render-mixin';
const Frontend = React.createClass({
......@@ -17,6 +18,7 @@ const Frontend = React.createClass({
<div style={{ flex: '0 0 auto', display: 'flex', width: '100%', height: '100%', background: '#1a1c23' }}>
<Drawer>
<div style={{ flex: '1 1 auto' }}>
<NavigationContainer />
<ComposeFormContainer />
<UploadFormContainer />
</div>
......@@ -32,6 +34,10 @@ const Frontend = React.createClass({
<Column icon='at' heading='Mentions'>
<StatusListContainer type='mentions' />
</Column>
<Column fluid={true}>
{this.props.children}
</Column>
</ColumnsArea>
<NotificationsContainer />
......
import PureRenderMixin from 'react-addons-pure-render-mixin';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Avatar from './avatar';
import IconButton from './icon_button';
import DisplayName from './display_name';
import { Link } from 'react-router';
const NavigationBar = React.createClass({
propTypes: {
account: ImmutablePropTypes.map.isRequired
},
mixins: [PureRenderMixin],
render () {
return (
<div style={{ padding: '10px', display: 'flex', cursor: 'default' }}>
<Avatar src={this.props.account.get('avatar')} size={40} />
<div style={{ flex: '1 1 auto', marginLeft: '8px' }}>
<strong style={{ fontWeight: '500', display: 'block' }}>{this.props.account.get('acct')}</strong>
<Link to='/settings' style={{ color: '#9baec8', textDecoration: 'none' }}>Settings <i className='fa fa fa-cog' /></Link>
</div>
</div>
);
}
});
export default NavigationBar;
......@@ -5,11 +5,13 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
import IconButton from './icon_button';
import DisplayName from './display_name';
import MediaGallery from './media_gallery';
import { hashHistory } from 'react-router';
const Status = React.createClass({
propTypes: {
status: ImmutablePropTypes.map.isRequired,
wrapped: React.PropTypes.bool,
onReply: React.PropTypes.func,
onFavourite: React.PropTypes.func,
onReblog: React.PropTypes.func
......@@ -29,6 +31,10 @@ const Status = React.createClass({
this.props.onReblog(this.props.status);
},
handleClick () {
hashHistory.push(`/statuses/${this.props.status.get('id')}`);
},
render () {
var content = { __html: this.props.status.get('content') };
var media = '';
......@@ -37,13 +43,13 @@ const Status = React.createClass({
if (status.get('reblog') !== null) {
return (
<div style={{ cursor: 'pointer' }}>
<div style={{ cursor: 'pointer' }} onClick={this.handleClick}>
<div style={{ marginLeft: '68px', color: '#616b86', padding: '8px 0', paddingBottom: '2px', fontSize: '14px', position: 'relative' }}>
<div style={{ position: 'absolute', 'left': '-26px'}}><i className='fa fa-fw fa-retweet'></i></div>
<a href={status.getIn(['account', 'url'])} className='status__display-name'><strong style={{ color: '#616b86'}}>{status.getIn(['account', 'display_name'])}</strong></a> reblogged
</div>
<Status {...other} status={status.get('reblog')} />
<Status {...other} wrapped={true} status={status.get('reblog')} />
</div>
);
}
......@@ -53,7 +59,7 @@ const Status = React.createClass({
}
return (
<div style={{ padding: '8px 10px', paddingLeft: '68px', position: 'relative', minHeight: '48px', borderBottom: '1px solid #363c4b', cursor: 'pointer' }}>
<div style={{ padding: '8px 10px', paddingLeft: '68px', position: 'relative', minHeight: '48px', borderBottom: '1px solid #363c4b', cursor: 'pointer' }} onClick={this.handleClick}>
<div style={{ fontSize: '15px' }}>
<div style={{ float: 'right', fontSize: '14px' }}>
<a href={status.get('url')} className='status__relative-time' style={{ color: '#616b86' }}><RelativeTimestamp timestamp={status.get('created_at')} /></a>
......
......@@ -27,7 +27,7 @@ const UploadButton = React.createClass({
<i className='fa fa-fw fa-photo' /> Add images
</Button>
<input ref='fileElement' type='file' onChange={this.handleChange} disabled={this.props.disabled} style={{ display: 'none' }} />
<input ref='fileElement' type='file' multiple={false} onChange={this.handleChange} disabled={this.props.disabled} style={{ display: 'none' }} />
</div>
);
}
......
import { connect } from 'react-redux';
import NavigationBar from '../components/navigation_bar';
const mapStateToProps = (state, props) => ({
account: state.getIn(['timelines', 'accounts', state.getIn(['timelines', 'me'])])
});
export default connect(mapStateToProps)(NavigationBar);
......@@ -3,25 +3,25 @@ import configureStore fro
import Frontend from '../components/frontend';
import { setTimeline, updateTimeline, deleteFromTimelines, refreshTimeline } from '../actions/timelines';
import { setAccessToken } from '../actions/meta';
import { setAccountSelf } from '../actions/accounts';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { Router, Route, createMemoryHistory } from 'react-router';
import AccountRoute from '../routes/account_route';
import StatusRoute from '../routes/status_route';
import { Router, Route, hashHistory } from 'react-router';
const store = configureStore();
const history = createMemoryHistory();
const store = configureStore();
const Root = React.createClass({
propTypes: {
token: React.PropTypes.string.isRequired,
timelines: React.PropTypes.object
timelines: React.PropTypes.object,
account: React.PropTypes.string
},
mixins: [PureRenderMixin],
componentWillMount() {
store.dispatch(setAccessToken(this.props.token));
store.dispatch(setAccountSelf(JSON.parse(this.props.account)));
for (var timelineType in this.props.timelines) {
if (this.props.timelines.hasOwnProperty(timelineType)) {
......@@ -53,10 +53,12 @@ const Root = React.createClass({
render () {
return (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Frontend}>
<Route path="/accounts/:account_id" component={AccountRoute} />
<Route path="/statuses/:status_id" component={StatusRoute} />
<Router history={hashHistory}>
<Route path='/' component={Frontend}>
<Route path='/settings' component={null} />
<Route path='/subscriptions' component={null} />
<Route path='/statuses/:statusId' component={null} />
<Route path='/accounts/:accountId' component={null} />
</Route>
</Router>
</Provider>
......
import { TIMELINE_SET, TIMELINE_UPDATE, TIMELINE_DELETE } from '../actions/timelines';
import { REBLOG_SUCCESS, FAVOURITE_SUCCESS } from '../actions/interactions';
import { ACCOUNT_SET_SELF } from '../actions/accounts';
import Immutable from 'immutable';
const initialState = Immutable.Map({
home: Immutable.List([]),
mentions: Immutable.List([]),
statuses: Immutable.Map(),
accounts: Immutable.Map()
accounts: Immutable.Map(),
me: null
});
function statusToMaps(state, status) {
......@@ -63,6 +65,11 @@ export default function timelines(state = initialState, action) {
case REBLOG_SUCCESS:
case FAVOURITE_SUCCESS:
return statusToMaps(state, Immutable.fromJS(action.response));
case ACCOUNT_SET_SELF:
return state.withMutations(map => {
map.setIn(['accounts', action.account.id], Immutable.fromJS(action.account));
map.set('me', action.account.id);
});
default:
return state;
}
......
const AccountRoute = React.createClass({
render() {
return (
<div>
{this.props.params.account_id}
</div>
)
}
});
export default AccountRoute;
const StatusRoute = React.createClass({
render() {
return (
<div>
{this.props.params.status_id}
</div>
)
}
});
export default StatusRoute;
module HomeHelper
def default_props
{
token: @token,
account: render(file: 'api/accounts/show', locals: { account: current_user.account }, formats: :json),
timelines: {
home: render(file: 'api/statuses/home', locals: { statuses: @home }, formats: :json),
mentions: render(file: 'api/statuses/mentions', locals: { statuses: @mentions }, formats: :json)
}
}
end
end
= react_component 'Root', { token: @token, timelines: { home: render(file: 'api/statuses/home', locals: { statuses: @home }, formats: :json), mentions: render(file: 'api/statuses/mentions', locals: { statuses: @mentions }, formats: :json) }}, class: 'app-holder', prerender: false
= react_component 'Root', default_props, class: 'app-holder', prerender: false
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment