Add tab bar alternative to desktop UI, upgrade react & react-redux
This commit is contained in:
		
							parent
							
								
									26390b1997
								
							
						
					
					
						commit
						0dac73b5cc
					
				
					 9 changed files with 103 additions and 32 deletions
				
			
		|  | @ -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 a new issue