Start navigation changes
This commit is contained in:
parent
61043357b5
commit
e0f97f4e99
8 changed files with 342 additions and 9 deletions
93
app/soapbox/components/primary_navigation.js
Normal file
93
app/soapbox/components/primary_navigation.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import IconWithCounter from 'soapbox/components/icon_with_counter';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
import { isStaff } from 'soapbox/utils/accounts';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const me = state.get('me');
|
||||
const reportsCount = state.getIn(['admin', 'openReports']).count();
|
||||
const approvalCount = state.getIn(['admin', 'awaitingApproval']).count();
|
||||
const instance = state.get('instance');
|
||||
const features = getFeatures(instance);
|
||||
|
||||
return {
|
||||
account: state.getIn(['accounts', me]),
|
||||
logo: getSoapboxConfig(state).get('logo'),
|
||||
notificationCount: state.getIn(['notifications', 'unread']),
|
||||
chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
|
||||
dashboardCount: reportsCount + approvalCount,
|
||||
features: getFeatures(instance),
|
||||
siteTitle: state.getIn(['instance', 'title']),
|
||||
federating: features.federating,
|
||||
};
|
||||
};
|
||||
|
||||
export default @withRouter @connect(mapStateToProps)
|
||||
class PrimaryNavigation extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
logo: PropTypes.string,
|
||||
account: ImmutablePropTypes.map,
|
||||
dashboardCount: PropTypes.number,
|
||||
notificationCount: PropTypes.number,
|
||||
chatsCount: PropTypes.number,
|
||||
features: PropTypes.object.isRequired,
|
||||
siteTitle: PropTypes.string,
|
||||
federating: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { account, features, notificationCount, chatsCount, dashboardCount } = this.props;
|
||||
|
||||
return (
|
||||
<div className='column-header__wrapper primary-navigation__wrapper'>
|
||||
<h1 className='column-header primary-navigation'>
|
||||
<NavLink to='/' exact className='btn grouped'>
|
||||
<Icon id='home' className='primary-navigation__icon' />
|
||||
<FormattedMessage id='tabs_bar.home' defaultMessage='Home' />
|
||||
</NavLink>
|
||||
|
||||
{account && <NavLink key='notifications' className='btn grouped' to='/notifications' data-preview-title-id='column.notifications'>
|
||||
<IconWithCounter icon='bell' count={notificationCount} />
|
||||
<FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' />
|
||||
</NavLink>}
|
||||
|
||||
{(features.chats && account) && <NavLink key='chats' className='btn grouped' to='/chats' data-preview-title-id='column.chats'>
|
||||
<IconWithCounter icon='comment' count={chatsCount} />
|
||||
<FormattedMessage id='tabs_bar.chats' defaultMessage='Chats' />
|
||||
</NavLink>}
|
||||
|
||||
{(account && isStaff(account)) && <NavLink key='dashboard' className='btn grouped' to='/admin' data-preview-title-id='tabs_bar.dashboard'>
|
||||
<IconWithCounter icon='tachometer' count={dashboardCount} />
|
||||
<FormattedMessage id='tabs_bar.dashboard' defaultMessage='Dashboard' />
|
||||
</NavLink>}
|
||||
|
||||
{/* <NavLink to='/timeline/local' className='btn grouped'>
|
||||
<Icon id={federating ? 'users' : 'globe-w'} fixedWidth className='column-header__icon' />
|
||||
{federating ? siteTitle : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
||||
</NavLink> */}
|
||||
|
||||
{/* federating && <NavLink to='/timeline/fediverse' className='btn grouped'>
|
||||
<Icon id='fediverse' fixedWidth className='column-header__icon' />
|
||||
<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />
|
||||
</NavLink> */}
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
79
app/soapbox/components/sub_navigation.js
Normal file
79
app/soapbox/components/sub_navigation.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
|
||||
const routes = [
|
||||
['status', { path: '/@:username/posts/:statusId' }],
|
||||
['account', { path: '/@:username' }],
|
||||
];
|
||||
|
||||
const findRouteType = path => {
|
||||
const route = routes.find(v => matchPath(path, v[1])) || [];
|
||||
return route[0];
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
status: { id: 'sub_navigation.status', defaultMessage: 'Post' },
|
||||
account: { id: 'sub_navigation.account', defaultMessage: 'Profile' },
|
||||
});
|
||||
|
||||
export default @withRouter
|
||||
@injectIntl
|
||||
class SubNavigation extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
handleBackClick = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
this.context.router.history.push('/');
|
||||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
handleBackKeyUp = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleClick();
|
||||
}
|
||||
}
|
||||
|
||||
getMessage = () => {
|
||||
const path = this.context.router.history.location.pathname;
|
||||
const type = findRouteType(path);
|
||||
|
||||
return messages[type] || null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props;
|
||||
const message = this.getMessage();
|
||||
|
||||
if (!message) return null;
|
||||
|
||||
return (
|
||||
<div className='sub-navigation'>
|
||||
<button
|
||||
className='sub-navigation__back'
|
||||
onClick={this.handleBackClick}
|
||||
onKeyUp={this.handleBackKeyUp}
|
||||
>
|
||||
<Icon id='chevron-left' />
|
||||
<FormattedMessage id='sub_navigation.back' defaultMessage='Back' />
|
||||
</button>
|
||||
<div className='sub-navigation__message'>
|
||||
{intl.formatMessage(message)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
44
app/soapbox/components/thumb_navigation.js
Normal file
44
app/soapbox/components/thumb_navigation.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
|
||||
export default
|
||||
@withRouter
|
||||
class ThumbNavigation extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='thumb-navigation'>
|
||||
<NavLink to='/' exact className='thumb-navigation__link'>
|
||||
<Icon id='home' />
|
||||
<span>
|
||||
<FormattedMessage id='navigation.home' defaultMessage='Home' />
|
||||
</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to='/search' className='thumb-navigation__link'>
|
||||
<Icon id='search' />
|
||||
<span>
|
||||
<FormattedMessage id='navigation.search' defaultMessage='Search' />
|
||||
</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to='/notifications' className='thumb-navigation__link'>
|
||||
<Icon id='bell' />
|
||||
<span>
|
||||
<FormattedMessage id='navigation.notifications' defaultMessage='Notifications' />
|
||||
</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to='/chats' className='thumb-navigation__link'>
|
||||
<Icon id='comment' />
|
||||
<span>
|
||||
<FormattedMessage id='navigation.chats' defaultMessage='Chats' />
|
||||
</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -45,6 +45,8 @@ import ProfileHoverCard from 'soapbox/components/profile_hover_card';
|
|||
import { getAccessToken } from 'soapbox/utils/auth';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { fetchCustomEmojis } from 'soapbox/actions/custom_emojis';
|
||||
import ThumbNavigation from 'soapbox/components/thumb_navigation';
|
||||
import SubNavigation from 'soapbox/components/sub_navigation';
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
|
||||
import {
|
||||
|
@ -651,6 +653,7 @@ class UI extends React.PureComponent {
|
|||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
||||
<div className={classnames} ref={this.setRef} style={style}>
|
||||
<TabsBar />
|
||||
<SubNavigation />
|
||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange} soapbox={soapbox}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
@ -668,6 +671,7 @@ class UI extends React.PureComponent {
|
|||
</BundleContainer>
|
||||
)}
|
||||
<ProfileHoverCard />
|
||||
<ThumbNavigation />
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
|
|
|
@ -5,7 +5,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import BundleContainer from '../features/ui/containers/bundle_container';
|
||||
import ComposeFormContainer from '../features/compose/containers/compose_form_container';
|
||||
import Avatar from '../components/avatar';
|
||||
import UserPanel from 'soapbox/features/ui/components/user_panel';
|
||||
// import UserPanel from 'soapbox/features/ui/components/user_panel';
|
||||
import PrimaryNavigation from 'soapbox/components/primary_navigation';
|
||||
import WhoToFollowPanel from 'soapbox/features/ui/components/who_to_follow_panel';
|
||||
import TrendsPanel from 'soapbox/features/ui/components/trends_panel';
|
||||
import PromoPanel from 'soapbox/features/ui/components/promo_panel';
|
||||
|
@ -57,13 +58,7 @@ class HomePage extends ImmutablePureComponent {
|
|||
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--left'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<UserPanel accountId={me} key='user-panel' />
|
||||
{showFundingPanel && <FundingPanel key='funding-panel' />}
|
||||
{showCryptoDonatePanel && (
|
||||
<BundleContainer fetchComponent={CryptoDonatePanel}>
|
||||
{Component => <Component limit={cryptoLimit} key='crypto-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<PrimaryNavigation />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -86,10 +81,17 @@ class HomePage extends ImmutablePureComponent {
|
|||
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--right'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
{/* <UserPanel accountId={me} key='user-panel' /> */}
|
||||
{showTrendsPanel && <TrendsPanel limit={3} key='trends-panel' />}
|
||||
{showWhoToFollowPanel && <WhoToFollowPanel limit={5} key='wtf-panel' />}
|
||||
{me ? <FeaturesPanel key='features-panel' /> : <SignUpPanel key='sign-up-panel' />}
|
||||
<PromoPanel key='promo-panel' />
|
||||
{showFundingPanel && <FundingPanel key='funding-panel' />}
|
||||
{showCryptoDonatePanel && (
|
||||
<BundleContainer fetchComponent={CryptoDonatePanel}>
|
||||
{Component => <Component limit={cryptoLimit} key='crypto-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<LinkFooter key='link-footer' />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
@import 'dyslexic';
|
||||
@import 'demetricator';
|
||||
@import 'chats';
|
||||
@import 'navigation';
|
||||
|
||||
// COMPONENTS
|
||||
@import 'components/buttons';
|
||||
|
|
110
app/styles/navigation.scss
Normal file
110
app/styles/navigation.scss
Normal file
|
@ -0,0 +1,110 @@
|
|||
.primary-navigation__wrapper {
|
||||
.primary-navigation {
|
||||
flex-direction: column;
|
||||
|
||||
> button,
|
||||
> .btn {
|
||||
justify-content: flex-start;
|
||||
font-size: 19px;
|
||||
font-weight: bold;
|
||||
|
||||
> i.fa,
|
||||
> .icon-with-counter {
|
||||
width: 25px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
> .svg-icon {
|
||||
width: 25px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
border-radius: 999px;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thumb-navigation {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
background: var(--foreground-color);
|
||||
justify-content: space-around;
|
||||
border-top: 1px solid hsla(var(--primary-text-color_hsl), 0.2);
|
||||
border-radius: 0;
|
||||
z-index: 999;
|
||||
|
||||
&__link {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
font-size: 20px;
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 895px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-navigation {
|
||||
position: sticky;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
color: var(--primary-text-color--faint);
|
||||
background: var(--foreground-color);
|
||||
justify-content: space-around;
|
||||
border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2);
|
||||
border-radius: 0;
|
||||
z-index: 999;
|
||||
|
||||
&__back {
|
||||
margin-right: auto;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-text-color--faint);
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 895px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -358,7 +358,7 @@
|
|||
z-index: 1000;
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 14px;
|
||||
bottom: 64px;
|
||||
right: 14px;
|
||||
width: 61px;
|
||||
height: 61px;
|
||||
|
|
Loading…
Reference in a new issue