forked from cybrespace/mastodon
Add tab bar alternative to desktop UI, upgrade react & react-redux
This commit is contained in:
parent
1bfbce7b45
commit
989c3f4002
|
@ -9,7 +9,6 @@ import {
|
||||||
import { updateNotifications } from '../actions/notifications';
|
import { updateNotifications } from '../actions/notifications';
|
||||||
import { setAccessToken } from '../actions/meta';
|
import { setAccessToken } from '../actions/meta';
|
||||||
import { setAccountSelf } from '../actions/accounts';
|
import { setAccountSelf } from '../actions/accounts';
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
|
||||||
import createBrowserHistory from 'history/lib/createBrowserHistory';
|
import createBrowserHistory from 'history/lib/createBrowserHistory';
|
||||||
import {
|
import {
|
||||||
applyRouterMiddleware,
|
applyRouterMiddleware,
|
||||||
|
@ -63,8 +62,6 @@ const Mastodon = React.createClass({
|
||||||
locale: React.PropTypes.string.isRequired
|
locale: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { token, account, locale } = this.props;
|
const { token, account, locale } = this.props;
|
||||||
|
|
||||||
|
@ -108,9 +105,9 @@ const Mastodon = React.createClass({
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
|
||||||
<Route path='/' component={UI}>
|
<Route path='/' component={UI}>
|
||||||
<IndexRedirect to="/getting_started" />
|
<IndexRedirect to="/getting-started" />
|
||||||
|
|
||||||
<Route path='getting_started' component={GettingStarted} />
|
<Route path='getting-started' component={GettingStarted} />
|
||||||
<Route path='timelines/home' component={HomeTimeline} />
|
<Route path='timelines/home' component={HomeTimeline} />
|
||||||
<Route path='timelines/mentions' component={MentionsTimeline} />
|
<Route path='timelines/mentions' component={MentionsTimeline} />
|
||||||
<Route path='timelines/public' component={PublicTimeline} />
|
<Route path='timelines/public' component={PublicTimeline} />
|
||||||
|
|
|
@ -1,26 +1,75 @@
|
||||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
import { Link } from 'react-router';
|
||||||
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
const style = {
|
const messages = defineMessages({
|
||||||
|
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||||
|
public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' },
|
||||||
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
|
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const outerStyle = {
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflowY: 'hidden'
|
||||||
|
};
|
||||||
|
|
||||||
|
const innerStyle = {
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
background: '#454b5e',
|
|
||||||
padding: '0',
|
padding: '0',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflowY: 'auto'
|
overflowY: 'auto',
|
||||||
|
flexGrow: '1'
|
||||||
};
|
};
|
||||||
|
|
||||||
const Drawer = React.createClass({
|
const tabStyle = {
|
||||||
|
display: 'block',
|
||||||
|
flex: '1 1 auto',
|
||||||
|
padding: '15px',
|
||||||
|
paddingBottom: '13px',
|
||||||
|
color: '#9baec8',
|
||||||
|
textDecoration: 'none',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '16px',
|
||||||
|
borderBottom: '2px solid transparent'
|
||||||
|
};
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
const tabActiveStyle = {
|
||||||
|
color: '#2b90d9',
|
||||||
|
borderBottom: '2px solid #2b90d9'
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
const Drawer = ({ children, withHeader, intl }) => {
|
||||||
return (
|
let header = '';
|
||||||
<div className='drawer' style={style}>
|
|
||||||
{this.props.children}
|
if (withHeader) {
|
||||||
|
header = (
|
||||||
|
<div className='drawer__header'>
|
||||||
|
<Link title={intl.formatMessage(messages.start)} style={tabStyle} to='/getting-started'><i className='fa fa-fw fa-bars' /></Link>
|
||||||
|
<Link title={intl.formatMessage(messages.public)} style={tabStyle} to='/timelines/public'><i className='fa fa-fw fa-globe' /></Link>
|
||||||
|
<a title={intl.formatMessage(messages.preferences)} style={tabStyle} href='/settings/preferences'><i className='fa fa-fw fa-cog' /></a>
|
||||||
|
<a title={intl.formatMessage(messages.logout)} style={tabStyle} href='/auth/sign_out' data-method='delete'><i className='fa fa-fw fa-sign-out' /></a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
return (
|
||||||
|
<div className='drawer' style={outerStyle}>
|
||||||
|
{header}
|
||||||
|
|
||||||
export default Drawer;
|
<div className='drawer__inner' style={innerStyle}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Drawer.propTypes = {
|
||||||
|
withHeader: React.PropTypes.bool,
|
||||||
|
children: React.PropTypes.node,
|
||||||
|
intl: React.PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default injectIntl(Drawer);
|
||||||
|
|
|
@ -21,7 +21,7 @@ const NavigationBar = React.createClass({
|
||||||
|
|
||||||
<div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}>
|
<div style={{ flex: '1 1 auto', marginLeft: '8px', color: '#9baec8' }}>
|
||||||
<strong style={{ fontWeight: '500', display: 'block', color: '#fff' }}>{this.props.account.get('acct')}</strong>
|
<strong style={{ fontWeight: '500', display: 'block', color: '#fff' }}>{this.props.account.get('acct')}</strong>
|
||||||
<a href='/settings/profile' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a> · <a href='/auth/sign_out' data-method='delete' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a>
|
<a href='/settings/profile' style={{ color: 'inherit', textDecoration: 'none' }}><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,8 @@ import { mountCompose, unmountCompose } from '../../actions/compose';
|
||||||
const Compose = React.createClass({
|
const Compose = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
dispatch: React.PropTypes.func.isRequired
|
dispatch: React.PropTypes.func.isRequired,
|
||||||
|
withHeader: React.PropTypes.bool
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
@ -25,7 +26,7 @@ const Compose = React.createClass({
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<Drawer>
|
<Drawer withHeader={this.props.withHeader}>
|
||||||
<SearchContainer />
|
<SearchContainer />
|
||||||
<NavigationContainer />
|
<NavigationContainer />
|
||||||
<ComposeFormContainer />
|
<ComposeFormContainer />
|
||||||
|
|
|
@ -30,7 +30,7 @@ const TabsBar = () => {
|
||||||
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/statuses/new'><i className='fa fa-fw fa-pencil' /> <FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></Link>
|
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/statuses/new'><i className='fa fa-fw fa-pencil' /> <FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></Link>
|
||||||
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/home'><i className='fa fa-fw fa-home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></Link>
|
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/timelines/home'><i className='fa fa-fw fa-home' /> <FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></Link>
|
||||||
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/notifications'><i className='fa fa-fw fa-bell' /> <FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></Link>
|
<Link style={tabStyle} activeStyle={tabActiveStyle} to='/notifications'><i className='fa fa-fw fa-bell' /> <FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></Link>
|
||||||
<Link style={{ ...tabStyle, flexGrow: '0', flexBasis: '30px' }} activeStyle={tabActiveStyle} to='/getting_started'><i className='fa fa-fw fa-bars' /></Link>
|
<Link style={{ ...tabStyle, flexGrow: '0', flexBasis: '30px' }} activeStyle={tabActiveStyle} to='/getting-started'><i className='fa fa-fw fa-bars' /></Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,11 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
const UI = React.createClass({
|
const UI = React.createClass({
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
dispatch: React.PropTypes.func.isRequired,
|
||||||
|
children: React.PropTypes.node
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState () {
|
getInitialState () {
|
||||||
return {
|
return {
|
||||||
width: window.innerWidth
|
width: window.innerWidth
|
||||||
|
@ -41,7 +46,7 @@ const UI = React.createClass({
|
||||||
handleDrop (e) {
|
handleDrop (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (e.dataTransfer) {
|
if (e.dataTransfer && e.dataTransfer.files.length === 1) {
|
||||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -72,7 +77,7 @@ const UI = React.createClass({
|
||||||
} else {
|
} else {
|
||||||
mountedColumns = (
|
mountedColumns = (
|
||||||
<ColumnsArea>
|
<ColumnsArea>
|
||||||
<Compose />
|
<Compose withHeader={true} />
|
||||||
<HomeTimeline trackScroll={false} />
|
<HomeTimeline trackScroll={false} />
|
||||||
<Notifications trackScroll={false} />
|
<Notifications trackScroll={false} />
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|
|
@ -349,6 +349,28 @@
|
||||||
width: 280px;
|
width: 280px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer__inner {
|
||||||
|
background: linear-gradient(rgba(69, 75, 94, 1), rgba(69, 75, 94, 0.65));
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer__header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 16px;
|
||||||
|
background: darken(#454b5e, 5%);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
a {
|
||||||
|
transition: all 100ms ease-in;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken(#454b5e, 10%);
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.column, .drawer {
|
.column, .drawer {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"chai-enzyme": "^0.5.2",
|
"chai-enzyme": "^0.5.2",
|
||||||
"css-loader": "^0.26.1",
|
"css-loader": "^0.26.1",
|
||||||
"emojione": "^2.2.6",
|
"emojione": "latest",
|
||||||
"enzyme": "^2.4.1",
|
"enzyme": "^2.4.1",
|
||||||
"es6-promise": "^3.2.1",
|
"es6-promise": "^3.2.1",
|
||||||
"http-link-header": "^0.5.0",
|
"http-link-header": "^0.5.0",
|
||||||
|
@ -39,22 +39,19 @@
|
||||||
"react-motion": "^0.4.5",
|
"react-motion": "^0.4.5",
|
||||||
"react-notification": "^6.4.0",
|
"react-notification": "^6.4.0",
|
||||||
"react-proxy": "^1.1.8",
|
"react-proxy": "^1.1.8",
|
||||||
"react-redux": "^5.0.0-beta.3",
|
"react-redux": "^5.0.1",
|
||||||
"react-redux-loading-bar": "^2.4.1",
|
"react-redux-loading-bar": "^2.4.1",
|
||||||
"react-router": "^2.8.0",
|
"react-router": "^2.8.0",
|
||||||
"react-router-scroll": "^0.3.2",
|
"react-router-scroll": "^0.3.2",
|
||||||
"react-simple-dropdown": "^1.1.4",
|
"react-simple-dropdown": "^1.1.4",
|
||||||
"react-storybook-addon-intl": "^0.1.0",
|
"react-storybook-addon-intl": "^0.1.0",
|
||||||
"react-toggle": "^2.1.1",
|
"react-toggle": "^2.1.1",
|
||||||
"redux": "^3.5.2",
|
"redux": "^3.6.0",
|
||||||
"redux-immutable": "^3.0.8",
|
"redux-immutable": "^3.0.8",
|
||||||
"redux-thunk": "^2.1.0",
|
"redux-thunk": "^2.1.0",
|
||||||
"reselect": "^2.5.4",
|
"reselect": "^2.5.4",
|
||||||
"sass-loader": "^4.0.2",
|
"sass-loader": "^4.0.2",
|
||||||
"sinon": "^1.17.6",
|
"sinon": "^1.17.6",
|
||||||
"style-loader": "^0.13.1"
|
"style-loader": "^0.13.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"emojione": "latest"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4301,9 +4301,9 @@ react-redux@^4.4.5:
|
||||||
lodash "^4.2.0"
|
lodash "^4.2.0"
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
react-redux@^5.0.0-beta.3:
|
react-redux@^5.0.1:
|
||||||
version "5.0.0-beta.3"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.0-beta.3.tgz#d50bfb00799cf7d2a9fd55fe34d6b3ecc24d3072"
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.1.tgz#84a41bd4cdd180452bb6922bc79ad25bd5abb7c4"
|
||||||
dependencies:
|
dependencies:
|
||||||
hoist-non-react-statics "^1.0.3"
|
hoist-non-react-statics "^1.0.3"
|
||||||
invariant "^2.0.0"
|
invariant "^2.0.0"
|
||||||
|
|
Loading…
Reference in New Issue