Skip to content

Commit

Permalink
Improve mobile layout (mastodon#1265)
Browse files Browse the repository at this point in the history
  • Loading branch information
dariusk committed Dec 31, 2022
1 parent bcfefcb commit 4f7f5a3
Show file tree
Hide file tree
Showing 12 changed files with 365 additions and 118 deletions.
26 changes: 7 additions & 19 deletions app/javascript/mastodon/features/compose/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import ComposeFormContainer from './containers/compose_form_container';
import NavigationContainer from './containers/navigation_container';
import ColumnHeader from 'mastodon/components/column_header';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
Expand Down Expand Up @@ -37,25 +38,6 @@ const mapStateToProps = (state, ownProps) => ({
showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : false,
});

let instanceMascot;
if (mascot) {
instanceMascot = <img alt='' draggable='false' src={mascot} />;
} else {
instanceMascot = <svg id='hometownlogo' width="2400" height="460" viewBox="0 0 2400 460" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
<g>
<g>
<path d="m326.20431,287.85649l-302.73044,0c-10.28698,0 -19.10436,8.81738 -19.10436,19.10436s8.81738,19.10436 19.10436,19.10436l302.73044,0c10.28698,0 19.10436,-8.81738 19.10436,-19.10436s-8.81738,-19.10436 -19.10436,-19.10436z"/>
<path d="m326.20431,351.04783l-302.73044,0c-10.28698,0 -19.10436,8.81738 -19.10436,19.10436s8.81738,19.10436 19.10436,19.10436l302.73044,0c10.28698,0 19.10436,-8.81738 19.10436,-19.10436s-8.81738,-19.10436 -19.10436,-19.10436z"/>
<path d="m326.20431,415.70867l-302.73044,0c-10.28698,0 -19.10436,8.81738 -19.10436,19.10436s8.81738,19.10436 19.10436,19.10436l302.73044,0c10.28698,0 19.10436,-8.81738 19.10436,-19.10436s-8.81738,-19.10436 -19.10436,-19.10436z"/>
<path d="m456.99565,287.85649c-10.28698,0 -19.10436,8.81738 -19.10436,19.10436l0,129.32173c0,10.28698 8.81738,19.10436 19.10436,19.10436s19.10436,-8.81738 19.10436,-19.10436l0,-129.32173c-1.46955,-11.75653 -10.28698,-19.10436 -19.10436,-19.10436z"/>
<path d="m392.33476,287.85649c-10.28698,0 -19.10436,8.81738 -19.10436,19.10436l0,129.32173c0,10.28698 8.81738,19.10436 19.10436,19.10436s19.10436,-8.81738 19.10436,-19.10436l0,-129.32173c-1.46955,-11.75653 -8.81738,-19.10436 -19.10436,-19.10436z"/>
<path d="m440.83045,205.56085c19.10436,-10.28698 29.39129,-36.73911 29.39129,-82.29564c0,-52.90436 -13.22609,-114.62609 -48.49564,-114.62609s-48.49564,61.72173 -48.49564,114.62609c0,45.55653 10.28698,72.00871 29.39129,82.29564l0,35.26955c0,10.28698 8.81738,19.10436 19.10436,19.10436s19.10436,-8.81738 19.10436,-19.10436l0,-35.26955l-0.00002,0zm-19.10436,-154.30436c5.87827,11.75653 11.75653,36.73911 11.75653,72.00871c0,36.73911 -7.34782,49.9652 -11.75653,49.9652s-11.75653,-13.22609 -11.75653,-49.9652c1.46955,-35.26955 7.34782,-60.25218 11.75653,-72.00871z"/>
<path d="m342.36956,123.26521c0,-1.46955 -1.46955,-1.46955 -1.46955,-2.93911l-47.02609,-60.25218c-2.93911,-4.40871 -8.81738,-7.34782 -14.69564,-7.34782l-23.51307,0l0,-27.92173c0,-10.28698 -8.81738,-19.10436 -19.10436,-19.10436s-19.10436,8.81738 -19.10436,19.10436l0,29.39129l-57.31307,0l-16.1652,0l-76.41738,0c-5.87827,0 -10.28698,2.93911 -14.69564,7.34782l-47.02609,60.25218c0,1.46955 -1.46955,1.46955 -1.46955,2.93911c0,0 -1.46955,1.46955 -1.46955,1.46955c1.46955,1.46955 1.46955,4.40871 1.46955,5.87827l0,108.74782c0,10.28698 8.81738,19.10436 19.10436,19.10436l76.41738,0l108.74782,0l117.56525,0c10.28698,0 19.10436,-8.81738 19.10436,-19.10436l0,-108.74782c0,-2.93911 0,-4.40871 -1.46955,-5.87827c-1.46955,-1.46955 -1.46955,-1.46955 -1.46955,-2.93911l-0.00005,0l-0.00002,0zm-224.84351,99.93045l-76.41738,0l0,-72.00871l149.89564,0l0,72.00871l-73.47827,0l0.00001,0zm99.93045,-108.74782l-17.6348,-23.51307l70.53916,0l17.6348,23.51307l-70.53916,0zm-64.66089,-23.51307l17.6348,23.51307l-110.21738,0l17.6348,-23.51307l74.94782,0l-0.00005,0l0.00001,0zm74.94782,60.25218l80.82609,0l0,72.00871l-80.82609,0l0,-72.00871z"/>
</g>
</g>
</svg>;
}

export default @connect(mapStateToProps)
@injectIntl
class Compose extends React.PureComponent {
Expand Down Expand Up @@ -155,6 +137,12 @@ class Compose extends React.PureComponent {

return (
<Column onFocus={this.onFocus}>
<ColumnHeader
icon='pencil'
title={intl.formatMessage(messages.compose)}
onClick={this.handleHeaderClick}
multiColumn={multiColumn}
/>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer />

Expand Down
2 changes: 1 addition & 1 deletion app/javascript/mastodon/features/explore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Explore extends React.PureComponent {
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon={isSearching ? 'search' : 'hashtag'}
icon='search'
title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)}
onClick={this.handleHeaderClick}
multiColumn={multiColumn}
Expand Down
14 changes: 12 additions & 2 deletions app/javascript/mastodon/features/ui/components/column_link.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { NavLink } from 'react-router-dom';
import Icon from 'mastodon/components/icon';
import classNames from 'classnames';

const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other }) => {
const className = classNames('column-link', { 'column-link--transparent': transparent });
const ColumnLink = ({ icon, text, to, href, method, badge, transparent, button, onClick, ...other }) => {
const className = classNames('column-link', { 'column-link--transparent': transparent, 'column-link--button': button });
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
const iconElement = typeof icon === 'string' ? <Icon id={icon} fixedWidth className='column-link__icon' /> : icon;

Expand All @@ -17,6 +17,14 @@ const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other
{badgeElement}
</a>
);
} else if (button) {
return (
<button className={className} onClick={onClick} title={text} {...other}>
{iconElement}
<span>{text}</span>
{badgeElement}
</button>
);
} else {
return (
<NavLink to={to} className={className} title={text} {...other}>
Expand All @@ -36,6 +44,8 @@ ColumnLink.propTypes = {
method: PropTypes.string,
badge: PropTypes.node,
transparent: PropTypes.bool,
button: PropTypes.bool,
onClick: PropTypes.function,
};

export default ColumnLink;
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,8 @@ export default class ColumnsArea extends ImmutablePureComponent {
<div className='columns-area columns-area--mobile'>{children}</div>
</div>

<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
<div className='columns-area__panels__pane__inner'>
<NavigationPanel />
</div>
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational columns-area__panels__pane__inner'>
<NavigationPanel />
</div>
</div>
);
Expand Down
16 changes: 1 addition & 15 deletions app/javascript/mastodon/features/ui/components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,8 @@ import React from 'react';
import Logo from 'mastodon/components/logo';
import { Link, withRouter } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import { registrationsOpen, me } from 'mastodon/initial_state';
import Avatar from 'mastodon/components/avatar';
import Permalink from 'mastodon/components/permalink';
import { registrationsOpen } from 'mastodon/initial_state';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

const Account = connect(state => ({
account: state.getIn(['accounts', me]),
}))(({ account }) => (
<Permalink href={account.get('url')} to={`/@${account.get('acct')}`} title={account.get('acct')}>
<Avatar account={account} size={35} />
</Permalink>
));

export default @withRouter
class Header extends React.PureComponent {
Expand All @@ -29,15 +18,12 @@ class Header extends React.PureComponent {

render () {
const { signedIn } = this.context.identity;
const { location } = this.props;

let content;

if (signedIn) {
content = (
<>
{location.pathname !== '/publish' && <Link to='/publish' className='button'><FormattedMessage id='compose_form.publish' defaultMessage='Publish' /></Link>}
<Account />
</>
);
} else {
Expand Down
139 changes: 91 additions & 48 deletions app/javascript/mastodon/features/ui/components/navigation_panel.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import Logo from 'mastodon/components/logo';
import { timelinePreview, showTrends } from 'mastodon/initial_state';
import { connect } from 'react-redux';
import Avatar from 'mastodon/components/avatar';
import Permalink from 'mastodon/components/permalink';
import { timelinePreview, showTrends, me } from 'mastodon/initial_state';
import ColumnLink from './column_link';
import DisabledAccountBanner from './disabled_account_banner';
import FollowRequestsColumnLink from './follow_requests_column_link';
Expand All @@ -18,6 +19,7 @@ const messages = defineMessages({
explore: { id: 'explore.title', defaultMessage: 'Explore' },
local: { id: 'tabs_bar.local_timeline', defaultMessage: 'Local' },
federated: { id: 'tabs_bar.federated_timeline', defaultMessage: 'Federated' },
menu: { id: 'navigation_bar.menu', defaultMessage: 'Menu' },
direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
Expand All @@ -26,11 +28,26 @@ const messages = defineMessages({
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
});

const Account = connect(state => ({
account: state.getIn(['accounts', me]),
}))(({ account }) => (
<Permalink className='column-link column-link--transparent navigation-panel--profile' href={account.get('url')} to={`/@${account.get('acct')}`} title={account.get('acct')}>
<Avatar account={account} size={32} inline />
<span>Profile</span>
</Permalink>
));

export default @injectIntl
class NavigationPanel extends React.Component {

constructor() {
super();
this.handleMenuToggle = this.handleMenuToggle.bind(this);
}

static contextTypes = {
router: PropTypes.object.isRequired,
identity: PropTypes.object.isRequired,
Expand All @@ -40,60 +57,86 @@ class NavigationPanel extends React.Component {
intl: PropTypes.object.isRequired,
};

state = {
retracted: false,
};

handleMenuToggle() {
this.setState({
retracted: !this.state.retracted,
});
const mainContent = document.querySelector('.columns-area--mobile');
if (!this.state.retracted) {
mainContent.classList.add('navigation-panel--retracted');
mainContent.classList.remove('navigation-panel--extended');
} else {
mainContent.classList.add('navigation-panel--extended');
mainContent.classList.remove('navigation-panel--retracted');
}
};

render () {
const { intl } = this.props;
const { signedIn, disabledAccountId } = this.context.identity;

const isWideSingleColumnLayout = document.querySelector('.columns-area__panels__pane--compositional') && window.getComputedStyle(document.querySelector('.columns-area__panels__pane--compositional')).display !== 'none';

return (
<div className='navigation-panel'>

{signedIn && (
<React.Fragment>
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<FollowRequestsColumnLink />
</React.Fragment>
)}

{showTrends ? (
<ColumnLink transparent to='/explore' icon='hashtag' text={intl.formatMessage(messages.explore)} />
) : (
<ColumnLink transparent to='/search' icon='search' text={intl.formatMessage(messages.search)} />
)}

{(signedIn || timelinePreview) && (
<>
<ColumnLink transparent to='/public/local' icon='users' text={intl.formatMessage(messages.local)} />
<ColumnLink transparent exact to='/public' icon='globe' text={intl.formatMessage(messages.federated)} />
</>
)}

{!signedIn && (
<div className='navigation-panel__sign-in-banner'>
<ColumnLink transparent button onClick={this.handleMenuToggle} icon='bars' text={intl.formatMessage(messages.menu)} />

{ (isWideSingleColumnLayout || !this.state.retracted) && <div id='navigation-retractable'>
{signedIn && (
<React.Fragment>
<Account />
<ColumnLink id='navigation-panel__publish' transparent to='/publish' icon='pencil' text={intl.formatMessage(messages.publish)} />
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<FollowRequestsColumnLink />
</React.Fragment>
)}

{showTrends ? (
<ColumnLink transparent to='/explore' icon='search' text={intl.formatMessage(messages.explore)} />
) : (
<ColumnLink transparent to='/search' icon='search' text={intl.formatMessage(messages.search)} />
)}

{(signedIn || timelinePreview) && (
<>
<ColumnLink transparent to='/public/local' icon='users' text={intl.formatMessage(messages.local)} />
<ColumnLink transparent exact to='/public' icon='globe' text={intl.formatMessage(messages.federated)} />
</>
)}

{!signedIn && (
<div className='navigation-panel__sign-in-banner'>
<hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
</div>
)}

{signedIn && (
<React.Fragment>
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />

<ListPanel />

<hr />

<ColumnLink transparent href='/settings/preferences' icon='cog' text={intl.formatMessage(messages.preferences)} />
</React.Fragment>
)}

<div className='navigation-panel__legal'>
<hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
<ColumnLink transparent href='/about' icon='ellipsis-h' text={intl.formatMessage(messages.about)} />
</div>
)}

{signedIn && (
<React.Fragment>
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />

<ListPanel />

<hr />

<ColumnLink transparent href='/settings/preferences' icon='cog' text={intl.formatMessage(messages.preferences)} />
</React.Fragment>
)}

<div className='navigation-panel__legal'>
<hr />
<ColumnLink transparent href='/about' icon='ellipsis-h' text={intl.formatMessage(messages.about)} />
</div>
}

<NavigationPortal />
</div>
Expand Down
4 changes: 0 additions & 4 deletions app/javascript/mastodon/features/ui/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,10 @@ import {
Directory,
Explore,
FollowRecommendations,
About,
PrivacyPolicy,
} from './util/async-components';
import initialState, { me, owner, singleUserMode, showTrends, trendsAsLanding } from '../../initial_state';
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
import Header from './components/header';

// Dummy import, to make sure that <Status /> ends up in the application bundle.
// Without this it ends up in ~8 very commonly used bundles.
Expand Down Expand Up @@ -569,8 +567,6 @@ class UI extends React.PureComponent {
return (
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
<Header />

<SwitchingColumnsArea location={location} mobile={layout === 'mobile' || layout === 'single-column'}>
{children}
</SwitchingColumnsArea>
Expand Down
1 change: 1 addition & 0 deletions app/javascript/mastodon/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@
"navigation_bar.follows_and_followers": "Follows and followers",
"navigation_bar.lists": "Lists",
"navigation_bar.logout": "Logout",
"navigation_bar.menu": "Menu",
"navigation_bar.mutes": "Muted users",
"navigation_bar.personal": "Personal",
"navigation_bar.pins": "Pinned posts",
Expand Down
13 changes: 13 additions & 0 deletions app/javascript/styles/fairy-floss/diff.scss
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,16 @@ a.mention,
border-top: 1px solid $purple3;
}
}

#navigation-panel__publish {
i {
color: $primary-text-color;
}
}

#navigation-panel__publish.active {
i {
background-color: lighten($ui-highlight-color, 10%);
color: $lighter-text-color;
}
}
Loading

0 comments on commit 4f7f5a3

Please sign in to comment.