Start navigation changes

profile-avatar-switcher
Alex Gleason 2021-09-12 18:16:53 -05:00
rodzic 61043357b5
commit e0f97f4e99
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
8 zmienionych plików z 342 dodań i 9 usunięć

Wyświetl plik

@ -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>
);
}
}

Wyświetl plik

@ -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>
);
}
}

Wyświetl plik

@ -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>
);
}
}

Wyświetl plik

@ -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>
);

Wyświetl plik

@ -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>

Wyświetl plik

@ -28,6 +28,7 @@
@import 'dyslexic';
@import 'demetricator';
@import 'chats';
@import 'navigation';
// COMPONENTS
@import 'components/buttons';

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -358,7 +358,7 @@
z-index: 1000;
display: none;
position: fixed;
bottom: 14px;
bottom: 64px;
right: 14px;
width: 61px;
height: 61px;