about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2019-05-23 01:35:22 +0200
committerThibG <thib@sitedethib.com>2019-06-13 22:15:31 +0200
commit610b4b44c4f70583f2f3082dc8f494fadb0681ef (patch)
tree4e7f102bb9fd89cadf698b903e63e3303e137f48 /app/javascript/flavours/glitch/features
parentfe5c4f976cf7f447a0db6a7e32fce992431fffd6 (diff)
[Glitch] Add single-column mode
Port 9ddeb30f90f9402eb567c88354d4956fcfdf0361 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
Diffstat (limited to 'app/javascript/flavours/glitch/features')
-rw-r--r--app/javascript/flavours/glitch/features/compose/index.js10
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/index.js17
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/columns_area.js10
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js24
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/tabs_bar.js27
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.js14
6 files changed, 61 insertions, 41 deletions
diff --git a/app/javascript/flavours/glitch/features/compose/index.js b/app/javascript/flavours/glitch/features/compose/index.js
index a7795a04d..d3070a199 100644
--- a/app/javascript/flavours/glitch/features/compose/index.js
+++ b/app/javascript/flavours/glitch/features/compose/index.js
@@ -61,12 +61,12 @@ class Compose extends React.PureComponent {
         <div className='drawer__pager'>
           {!isSearchPage && <div className='drawer__inner'>
             <NavigationContainer />
+
             <ComposeFormContainer />
-            {multiColumn && (
-              <div className='drawer__inner__mastodon'>
-                {mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' onClick={onClickElefriend} />}
-              </div>
-            )}
+
+            <div className='drawer__inner__mastodon'>
+              {mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' onClick={onClickElefriend} />}
+            </div>
           </div>}
 
           <Motion defaultStyle={{ x: isSearchPage ? 0 : -100 }} style={{ x: spring(showSearch || isSearchPage ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js
index 7b645c9d0..25fff1974 100644
--- a/app/javascript/flavours/glitch/features/getting_started/index.js
+++ b/app/javascript/flavours/glitch/features/getting_started/index.js
@@ -10,10 +10,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { me, invitesEnabled, version } from 'flavours/glitch/util/initial_state';
 import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
+import { changeSetting } from 'flavours/glitch/actions/settings';
 import { List as ImmutableList } from 'immutable';
 import { createSelector } from 'reselect';
 import { fetchLists } from 'flavours/glitch/actions/lists';
 import { preferencesLink, profileLink, signOutLink } from 'flavours/glitch/util/backend_links';
+import Toggle from 'react-toggle';
 
 const messages = defineMessages({
   heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@@ -52,6 +54,7 @@ const makeMapStateToProps = () => {
     columns: state.getIn(['settings', 'columns']),
     unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
     unreadNotifications: state.getIn(['notifications', 'unread']),
+    forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false),
   });
 
   return mapStateToProps;
@@ -61,6 +64,7 @@ const mapDispatchToProps = dispatch => ({
   fetchFollowRequests: () => dispatch(fetchFollowRequests()),
   fetchLists: () => dispatch(fetchLists()),
   openSettings: () => dispatch(openModal('SETTINGS', {})),
+  changeForceSingleColumn: checked => dispatch(changeSetting(['forceSingleColumn'], checked)),
 });
 
 const badgeDisplay = (number, limit) => {
@@ -88,6 +92,8 @@ export default class GettingStarted extends ImmutablePureComponent {
     lists: ImmutablePropTypes.list,
     fetchLists: PropTypes.func.isRequired,
     openSettings: PropTypes.func.isRequired,
+    forceSingleColumn: PropTypes.bool,
+    changeForceSingleColumn: PropTypes.func.isRequired,
   };
 
   componentWillMount () {
@@ -102,8 +108,12 @@ export default class GettingStarted extends ImmutablePureComponent {
     }
   }
 
+  handleForceSingleColumnChange = ({ target }) => {
+    this.props.changeForceSingleColumn(target.checked);
+  }
+
   render () {
-    const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings } = this.props;
+    const { intl, myAccount, columns, multiColumn, unreadFollowRequests, unreadNotifications, lists, openSettings, forceSingleColumn } = this.props;
 
     const navItems = [];
     let listItems = [];
@@ -183,6 +193,11 @@ export default class GettingStarted extends ImmutablePureComponent {
             </p>
           </div>
         </div>
+
+        <label className='navigational-toggle'>
+          <FormattedMessage id='getting_started.use_simple_layout' defaultMessage='Use simple layout' />
+          <Toggle checked={forceSingleColumn} onChange={this.handleForceSingleColumnChange} />
+        </label>
       </Column>
     );
   }
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
index 0fe580b9b..5e680a61c 100644
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
@@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 
 import ReactSwipeableViews from 'react-swipeable-views';
-import { links, getIndex, getLink } from './tabs_bar';
+import TabsBar, { links, getIndex, getLink } from './tabs_bar';
 import { Link } from 'react-router-dom';
 
 import BundleContainer from '../containers/bundle_container';
@@ -139,7 +139,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
       <ColumnLoading title={title} icon={icon} />;
 
     return (
-      <div className='columns-area' key={index}>
+      <div className='columns-area columns-area--mobile' key={index}>
         {view}
       </div>
     );
@@ -164,13 +164,17 @@ export default class ColumnsArea extends ImmutablePureComponent {
       const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><i className='fa fa-pencil' /></Link>;
 
       return columnIndex !== -1 ? [
+        <TabsBar key='tabs' />,
+
         <ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={!swipeToChangeColumns}>
           {links.map(this.renderView)}
         </ReactSwipeableViews>,
 
         floatingActionButton,
       ] : [
-        <div className='columns-area'>{children}</div>,
+        <TabsBar key='tabs' />,
+
+        <div key='content' className='columns-area columns-area--mobile'>{children}</div>,
 
         floatingActionButton,
       ];
diff --git a/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js
new file mode 100644
index 000000000..137658b94
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/ui/components/notifications_counter_icon.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import Icon from 'flavours/glitch/components/icon';
+
+const mapStateToProps = state => ({
+  count: state.getIn(['notifications', 'unread']),
+  showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']),
+});
+
+const formatNumber = num => num > 99 ? '99+' : num;
+
+const NotificationsCounterIcon = ({ count, showBadge }) => (
+  <i className='icon-with-badge'>
+    <Icon icon='bell' fixedWidth />
+    {showBadge && count > 0 && <i className='icon-with-badge__badge'>{formatNumber(count)}</i>}
+  </i>
+);
+
+NotificationsCounterIcon.propTypes = {
+  count: PropTypes.number.isRequired,
+};
+
+export default connect(mapStateToProps)(NotificationsCounterIcon);
diff --git a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
index b44a21a42..8963b16b3 100644
--- a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
+++ b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js
@@ -4,34 +4,11 @@ import { NavLink, withRouter } from 'react-router-dom';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import { debounce } from 'lodash';
 import { isUserTouching } from 'flavours/glitch/util/is_mobile';
-import { connect } from 'react-redux';
-
-const mapStateToProps = state => ({
-  unreadNotifications: state.getIn(['notifications', 'unread']),
-  showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']),
-});
-
-@connect(mapStateToProps)
-class NotificationsIcon extends React.PureComponent {
-  static propTypes = {
-    unreadNotifications: PropTypes.number,
-    showBadge: PropTypes.bool,
-  };
-
-  render() {
-    const { unreadNotifications, showBadge } = this.props;
-    return (
-      <span className='icon-badge-wrapper'>
-        <i className='fa fa-fw fa-bell' />
-        { showBadge && unreadNotifications > 0 && <div className='icon-badge' />}
-      </span>
-    );
-  }
-}
+import NotificationsCounterIcon from './notifications_counter_icon';
 
 export const links = [
   <NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
-  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
+  <NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
 
   <NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
   <NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js
index f8fff934d..36156a5ef 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.js
@@ -2,7 +2,6 @@ import React from 'react';
 import NotificationsContainer from './containers/notifications_container';
 import PropTypes from 'prop-types';
 import LoadingBarContainer from './containers/loading_bar_container';
-import TabsBar from './components/tabs_bar';
 import ModalContainer from './containers/modal_container';
 import { connect } from 'react-redux';
 import { Redirect, withRouter } from 'react-router-dom';
@@ -69,6 +68,7 @@ const mapStateToProps = state => ({
   unreadNotifications: state.getIn(['notifications', 'unread']),
   showFaviconBadge: state.getIn(['local_settings', 'notifications', 'favicon_badge']),
   hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']),
+  forceSingleColumn: state.getIn(['settings', 'forceSingleColumn'], false),
 });
 
 const keyMap = {
@@ -126,6 +126,7 @@ export default class UI extends React.Component {
     dropdownMenuIsOpen: PropTypes.bool,
     unreadNotifications: PropTypes.number,
     showFaviconBadge: PropTypes.bool,
+    forceSingleColumn: PropTypes.bool,
   };
 
   state = {
@@ -431,7 +432,9 @@ export default class UI extends React.Component {
 
   render () {
     const { width, draggingOver } = this.state;
-    const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen } = this.props;
+    const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen, forceSingleColumn } = this.props;
+    const singleColumn = forceSingleColumn || isMobile(width, layout);
+    const redirect = singleColumn ? <Redirect from='/' to='/timelines/home' exact /> : <Redirect from='/' to='/getting-started' exact />;
 
     const columnsClass = layout => {
       switch (layout) {
@@ -475,11 +478,9 @@ export default class UI extends React.Component {
     return (
       <HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
         <div className={className} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
-          {navbarUnder ? null : (<TabsBar />)}
-
-          <ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
+          <ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={singleColumn}>
             <WrappedSwitch>
-              <Redirect from='/' to='/getting-started' exact />
+              {redirect}
               <WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
               <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
               <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
@@ -518,7 +519,6 @@ export default class UI extends React.Component {
           </ColumnsAreaContainer>
 
           <NotificationsContainer />
-          {navbarUnder ? (<TabsBar />) : null}
           <LoadingBarContainer className='loading-bar' />
           <ModalContainer />
           <UploadArea active={draggingOver} onClose={this.closeUploadModal} />