diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2019-05-23 01:35:22 +0200 |
---|---|---|
committer | multiple creatures <dev@multiple-creature.party> | 2020-02-22 01:20:15 -0600 |
commit | 687fa7b9edd6a8b46eb6f12d7db8abde76e1eaf2 (patch) | |
tree | 1425400adff34864d76984e7722eb88fa285c9bf /app/javascript | |
parent | e67f7b213055f996de7ab8c80ec19ee4a6dc3b51 (diff) |
port glitch-soc@`610b4b4` to monsterfork: [Glitch] Add single-column mode
Port 9ddeb30f90f9402eb567c88354d4956fcfdf0361 to glitch-soc Signed-off-by: Thibaut Girka <thib@sitedethib.com>
Diffstat (limited to 'app/javascript')
11 files changed, 247 insertions, 97 deletions
diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.js index 5fc952d8e..a6ae3bc75 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_input.js +++ b/app/javascript/flavours/glitch/components/autosuggest_input.js @@ -49,7 +49,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { autoFocus: PropTypes.bool, className: PropTypes.string, id: PropTypes.string, - searchTokens: PropTypes.arrayOf(PropTypes.string), + searchTokens: ImmutablePropTypes.list, maxLength: PropTypes.number, }; diff --git a/app/javascript/flavours/glitch/features/compose/index.js b/app/javascript/flavours/glitch/features/compose/index.js index 2e50738ed..7a0b66754 100644 --- a/app/javascript/flavours/glitch/features/compose/index.js +++ b/app/javascript/flavours/glitch/features/compose/index.js @@ -61,26 +61,12 @@ class Compose extends React.PureComponent { <div className='drawer__pager'> {!isSearchPage && <div className='drawer__inner'> <NavigationContainer /> + <ComposeFormContainer /> - {isStaff && multiColumn && ( - <div className='drawer__inner__admin'> - <h2>Staff Tools</h2> - <ul> - <li><a href="/admin/action_logs" target="_blank" rel="nofollow noopener">Audit log</a></li> - <li><a href="/admin/reports" target="_blank" rel="nofollow noopener">Reports</a></li> - <li><a href="/admin/pending_accounts" target="_blank" rel="nofollow noopener">Pending accounts</a></li> - <li><a href="/admin/domain_blocks/new" target="_blank" rel="nofollow noopener">Add domain policy...</a></li> - <li><a href="/admin/instances" target="_blank" rel="nofollow noopener">Federation</a></li> - <li><a href="/admin/accounts" target="_blank" rel="nofollow noopener">Accounts</a></li> - <li><a href="/admin/custom_emojis" target="_blank" rel="nofollow noopener">Custom emojis</a></li> - </ul> - </div> - )} - {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 dcca5cf50..b8bb330f2 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 @@ 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 @@ 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 7513779cb..fca437997 100644 --- a/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js +++ b/app/javascript/flavours/glitch/features/ui/components/tabs_bar.js @@ -4,33 +4,10 @@ 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='/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 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 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>, diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 7ad53669a..585c92f34 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} /> @@ -519,7 +520,6 @@ export default class UI extends React.Component { </ColumnsAreaContainer> <NotificationsContainer /> - {navbarUnder ? (<TabsBar />) : null} <LoadingBarContainer className='loading-bar' /> <ModalContainer /> <UploadArea active={draggingOver} onClose={this.closeUploadModal} /> diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js index 0ce51ef97..7340aa86c 100644 --- a/app/javascript/flavours/glitch/reducers/settings.js +++ b/app/javascript/flavours/glitch/reducers/settings.js @@ -15,6 +15,8 @@ const initialState = ImmutableMap({ skinTone: 1, + forceSingleColumn: false, + home: ImmutableMap({ shows: ImmutableMap({ reblog: true, diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 0bf01ed8d..50438393e 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -13,16 +13,6 @@ position: relative; } -@include limited-single-column('screen and (min-width: 360px)', $parent: null) { - .columns-area { - padding: 10px; - } - - .react-swipeable-view-container .columns-area { - height: calc(100% - 20px) !important; - } -} - .react-swipeable-view-container { &, .columns-area, @@ -63,34 +53,6 @@ overflow: hidden; } -@include limited-single-column('screen and (min-width: 360px)', $parent: null) { - .tabs-bar { - margin: 10px; - margin-bottom: 0; - } -} - -:root { // Overrides .wide stylings for mobile view - @include single-column('screen and (max-width: 630px)', $parent: null) { - .column { - flex: auto; - width: 100%; - min-width: 0; - max-width: none; - padding: 0; - } - - .columns-area { - flex-direction: column; - } - - .search__input, - .autosuggest-textarea__textarea { - font-size: 16px; - } - } -} - @include multi-columns('screen and (min-width: 631px)', $parent: null) { .columns-area { padding: 0; @@ -442,6 +404,10 @@ contain: strict; } + & > span { + max-width: 400px; + } + a { color: $highlight-text-color; text-decoration: none; diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss index b05f231f2..167b90788 100644 --- a/app/javascript/flavours/glitch/styles/components/drawer.scss +++ b/app/javascript/flavours/glitch/styles/components/drawer.scss @@ -279,6 +279,7 @@ .drawer__inner__mastodon { flex: 1; min-height: 47px; + display: none; > img { display: block; @@ -298,6 +299,10 @@ border: none; cursor: inherit; } + + @media screen and (min-height: 640px) { + display: block; + } } .pseudo-drawer { diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 3a5868759..4861bf492 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -553,6 +553,7 @@ } .tabs-bar { + box-sizing: border-box; display: flex; background: lighten($ui-base-color, 8%); flex: 0 0 auto; @@ -590,15 +591,130 @@ } } - span:last-child { + span { margin-left: 5px; display: none; } } -@include multi-columns('screen and (min-width: 631px)', $parent: null) { +@media screen and (min-width: 600px) { + .tabs-bar__link { + span { + display: inline; + } + } +} + +.columns-area--mobile { + flex-direction: column; + width: 100%; + max-width: 600px; + margin: 0 auto; + + .column, + .drawer { + width: 100%; + height: 100%; + padding: 0; + } + + .search__input, + .autosuggest-textarea__textarea { + font-size: 16px; + } + + @media screen and (min-width: 360px) { + padding: 10px; + } + + @media screen and (min-width: 630px) { + .detailed-status { + padding: 15px; + + .media-gallery, + .video-player { + margin-top: 15px; + } + } + + .account__header__bar { + padding: 5px 10px; + } + + .navigation-bar, + .compose-form { + padding: 15px; + } + + .compose-form .compose-form__publish .compose-form__publish-button-wrapper { + padding-top: 15px; + } + + .status { + padding: 15px 15px 15px (48px + 15px * 2); + min-height: 48px + 2px; + + &__avatar { + left: 15px; + top: 17px; + } + + &__content { + padding-top: 5px; + } + + &__prepend { + margin-left: 48px + 15px * 2; + padding-top: 15px; + } + + &__prepend-icon-wrapper { + left: -32px; + } + + .media-gallery, + &__action-bar, + .video-player { + margin-top: 10px; + } + } + } +} + +@media screen and (min-width: 360px) { .tabs-bar { - display: none; + margin: 10px auto; + margin-bottom: 0; + width: calc(100% - 20px); + max-width: 600px; + } + + .react-swipeable-view-container .columns-area--mobile { + height: calc(100% - 20px) !important; + } + + .getting-started__wrapper, + .getting-started__trends, + .search { + margin-bottom: 10px; + } +} + +.icon-with-badge { + position: relative; + + &__badge { + position: absolute; + right: -13px; + top: -13px; + background: $ui-highlight-color; + border: 2px solid lighten($ui-base-color, 8%); + padding: 1px 6px; + border-radius: 6px; + font-size: 10px; + font-weight: 500; + line-height: 14px; + color: $primary-text-color; } } @@ -1268,6 +1384,61 @@ height: 1em; } +.navigational-toggle { + padding: 10px; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 14px; + color: $dark-text-color; +} + +.layout-toggle { + display: flex; + padding: 5px; + + button { + box-sizing: border-box; + flex: 0 0 50%; + background: transparent; + padding: 5px; + border: 0; + position: relative; + + &:hover, + &:focus, + &:active { + svg path:first-child { + fill: lighten($ui-base-color, 16%); + } + } + } + + svg { + width: 100%; + height: auto; + + path:first-child { + fill: lighten($ui-base-color, 12%); + } + + path:last-child { + fill: darken($ui-base-color, 14%); + } + } + + &__active { + color: $ui-highlight-color; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: lighten($ui-base-color, 12%); + border-radius: 50%; + padding: 0.35rem; + } +} + ::-webkit-scrollbar-thumb { border-radius: 0; } |