Adding public timeline

This commit is contained in:
Eugen Rochko 2016-10-07 16:00:11 +02:00
parent 06016453bd
commit 1f650d327d
21 changed files with 229 additions and 71 deletions

View file

@ -27,9 +27,10 @@ import StatusList from '../../components/status_list';
import LoadingIndicator from '../../components/loading_indicator';
import Immutable from 'immutable';
import ActionBar from './components/action_bar';
import Column from '../ui/components/column';
function selectStatuses(state, accountId) {
return state.getIn(['timelines', 'accounts_timelines', accountId], Immutable.List()).map(id => selectStatus(state, id)).filterNot(status => status === null);
return state.getIn(['timelines', 'accounts_timelines', accountId], Immutable.List([])).map(id => selectStatus(state, id)).filterNot(status => status === null);
};
const mapStateToProps = (state, props) => ({
@ -109,15 +110,21 @@ const Account = React.createClass({
const { account, statuses, me } = this.props;
if (account === null) {
return <LoadingIndicator />;
return (
<Column>
<LoadingIndicator />
</Column>
);
}
return (
<div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
<Header account={account} />
<ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} />
<StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
</div>
<Column>
<div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
<Header account={account} />
<ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} />
<StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} onDelete={this.handleDelete} />
</div>
</Column>
);
}

View file

@ -1,12 +1,16 @@
import Column from '../ui/components/column';
const GettingStarted = () => {
return (
<div className='static-content'>
<h1>Getting started</h1>
<p>Mastodon is still in development and one of the lacking areas at the moment is user discovery.</p>
<p>You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form in the bottom of the sidebar.</p>
<p>If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.</p>
<p>The developer of this project can be followed as Gargron@mastodon.social</p>
</div>
<Column>
<div className='static-content'>
<h1>Getting started</h1>
<p>Mastodon is still in development and one of the lacking areas at the moment is user discovery.</p>
<p>You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form in the bottom of the sidebar.</p>
<p>If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.</p>
<p>The developer of this project can be followed as Gargron@mastodon.social</p>
</div>
</Column>
);
};

View file

@ -0,0 +1,103 @@
import { connect } from 'react-redux';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import ImmutablePropTypes from 'react-immutable-proptypes';
import StatusList from '../../components/status_list';
import Column from '../ui/components/column';
import Immutable from 'immutable';
import { selectStatus } from '../../reducers/timelines';
import {
updateTimeline,
refreshTimeline,
expandTimeline
} from '../../actions/timelines';
import { deleteStatus } from '../../actions/statuses';
import { replyCompose } from '../../actions/compose';
import {
favourite,
reblog,
unreblog,
unfavourite
} from '../../actions/interactions';
function selectStatuses(state) {
return state.getIn(['timelines', 'public'], Immutable.List()).map(id => selectStatus(state, id)).filterNot(status => status === null);
};
const mapStateToProps = (state) => ({
statuses: selectStatuses(state),
me: state.getIn(['timelines', 'me'])
});
const PublicTimeline = React.createClass({
propTypes: {
statuses: ImmutablePropTypes.list.isRequired,
me: React.PropTypes.number.isRequired,
dispatch: React.PropTypes.func.isRequired
},
mixins: [PureRenderMixin],
componentWillMount () {
const { dispatch } = this.props;
dispatch(refreshTimeline('public'));
if (typeof App !== 'undefined') {
this.subscription = App.cable.subscriptions.create('PublicChannel', {
received (data) {
dispatch(updateTimeline('public', JSON.parse(data.message)));
}
});
}
},
componentWillUnmount () {
if (typeof this.subscription !== 'undefined') {
this.subscription.unsubscribe();
}
},
handleReply (status) {
this.props.dispatch(replyCompose(status));
},
handleReblog (status) {
if (status.get('reblogged')) {
this.props.dispatch(unreblog(status));
} else {
this.props.dispatch(reblog(status));
}
},
handleFavourite (status) {
if (status.get('favourited')) {
this.props.dispatch(unfavourite(status));
} else {
this.props.dispatch(favourite(status));
}
},
handleDelete (status) {
this.props.dispatch(deleteStatus(status.get('id')));
},
handleScrollToBottom () {
this.props.dispatch(expandTimeline('public'));
},
render () {
const { statuses, me } = this.props;
return (
<Column icon='globe' heading='Public'>
<StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} onDelete={this.handleDelete} />
</Column>
);
},
});
export default connect(mapStateToProps)(PublicTimeline);

View file

@ -7,6 +7,7 @@ import EmbeddedStatus from '../../components/status';
import LoadingIndicator from '../../components/loading_indicator';
import DetailedStatus from './components/detailed_status';
import ActionBar from './components/action_bar';
import Column from '../ui/components/column';
import { favourite, reblog } from '../../actions/interactions';
import { replyCompose } from '../../actions/compose';
import { selectStatus } from '../../reducers/timelines';
@ -64,20 +65,26 @@ const Status = React.createClass({
const { status, ancestors, descendants, me } = this.props;
if (status === null) {
return <LoadingIndicator />;
return (
<Column>
<LoadingIndicator />
</Column>
);
}
const account = status.get('account');
return (
<div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable'>
<div>{this.renderChildren(ancestors)}</div>
<Column>
<div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable'>
<div>{this.renderChildren(ancestors)}</div>
<DetailedStatus status={status} me={me} />
<ActionBar status={status} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} />
<DetailedStatus status={status} me={me} />
<ActionBar status={status} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} />
<div>{this.renderChildren(descendants)}</div>
</div>
<div>{this.renderChildren(descendants)}</div>
</div>
</Column>
);
}

View file

@ -29,7 +29,6 @@ const scrollTop = (node) => {
};
};
const Column = React.createClass({
propTypes: {
@ -50,10 +49,6 @@ const Column = React.createClass({
}
},
handleScroll () {
// todo
},
render () {
let header = '';
@ -61,10 +56,10 @@ 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', marginBottom: '0', display: 'flex', flexDirection: 'column' };
const style = { width: '330px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', marginBottom: '0', display: 'flex', flexDirection: 'column' };
return (
<div style={style} onWheel={this.handleWheel} onScroll={this.handleScroll}>
<div style={style} onWheel={this.handleWheel}>
{header}
{this.props.children}
</div>

View file

@ -6,7 +6,7 @@ const ColumnsArea = React.createClass({
render () {
return (
<div style={{ display: 'flex', flexDirection: 'row', flex: '1', marginRight: '10px', marginBottom: '10px', overflowX: 'auto' }}>
<div style={{ display: 'flex', flexDirection: 'row', flex: '1', justifyContent: 'flex-start', marginRight: '10px', marginBottom: '10px', overflowX: 'auto' }}>
{this.props.children}
</div>
);

View file

@ -19,7 +19,7 @@ const NavigationBar = React.createClass({
<div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}>
<strong style={{ fontWeight: '500', display: 'block', color: '#fff' }}>{this.props.account.get('acct')}</strong>
<a href='/settings' style={{ color: 'inherit', textDecoration: 'none' }}>Settings</a> · <a href='/auth/sign_out' data-method='delete' style={{ color: 'inherit', textDecoration: 'none' }}>Logout</a>
<a href='/settings' style={{ color: 'inherit', textDecoration: 'none' }}>Settings</a> · <Link to='/statuses/all' style={{ color: 'inherit', textDecoration: 'none' }}>Public timeline</Link> · <a href='/auth/sign_out' data-method='delete' style={{ color: 'inherit', textDecoration: 'none' }}>Logout</a>
</div>
</div>
);

View file

@ -40,9 +40,7 @@ const UI = React.createClass({
<StatusListContainer type='mentions' />
</Column>
<Column>
{this.props.children}
</Column>
{this.props.children}
</ColumnsArea>
<NotificationsContainer />