Added sidebar menu feature
This commit is contained in:
parent
915f7f891a
commit
0be86d6ec5
|
@ -0,0 +1,14 @@
|
|||
export const SIDEBAR_OPEN = 'SIDEBAR_OPEN';
|
||||
export const SIDEBAR_CLOSE = 'SIDEBAR_CLOSE';
|
||||
|
||||
export function openSidebar() {
|
||||
return {
|
||||
type: SIDEBAR_OPEN,
|
||||
};
|
||||
};
|
||||
|
||||
export function closeSidebar() {
|
||||
return {
|
||||
type: SIDEBAR_CLOSE,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,161 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import Avatar from './avatar';
|
||||
import IconButton from './icon_button';
|
||||
import Icon from './icon';
|
||||
import DisplayName from './display_name';
|
||||
import { closeSidebar } from '../actions/sidebar';
|
||||
import { shortNumberFormat } from '../utils/numbers';
|
||||
import { me } from '../initial_state';
|
||||
import { makeGetAccount } from '../selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
profile: { id: 'account.profile', defaultMessage: 'Profile' },
|
||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||
lists: { id: 'column.lists', defaultMessage: 'Lists', },
|
||||
apps: { id: 'tabs_bar.apps', defaultMessage: 'Apps' },
|
||||
news: { id: 'tabs_bar.news', defaultMessage: 'News' },
|
||||
})
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
return {
|
||||
account: getAccount(state, me),
|
||||
sidebarOpen: state.get('sidebar').sidebarOpen,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose () {
|
||||
dispatch(closeSidebar());
|
||||
},
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class SidebarMenu extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
account: ImmutablePropTypes.map,
|
||||
sidebarOpen: PropTypes.bool,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { sidebarOpen, onClose, intl, account } = this.props;
|
||||
const acct = account.get('acct');
|
||||
|
||||
const classes = classNames('sidebar-menu__root', {
|
||||
'sidebar-menu__root--visible': sidebarOpen,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className='sidebar-menu__wrapper' role='button' onClick={onClose} />
|
||||
<div className='sidebar-menu'>
|
||||
|
||||
<div className='sidebar-menu-header'>
|
||||
<span className='sidebar-menu-header__title'>Account Info</span>
|
||||
<IconButton title='close' onClick={onClose} icon='close' className='sidebar-menu-header__btn' />
|
||||
</div>
|
||||
|
||||
<div className='sidebar-menu__content'>
|
||||
|
||||
<div className='sidebar-menu-profile'>
|
||||
<div className='sidebar-menu-profile__avatar'>
|
||||
<Link to={`/${acct}}`} title={acct} onClick={onClose}>
|
||||
<Avatar account={account} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className='sidebar-menu-profile__name'>
|
||||
<DisplayName account={account}/>
|
||||
</div>
|
||||
|
||||
<div className='sidebar-menu-profile__stats'>
|
||||
<NavLink className='sidebar-menu-profile-stat' to={`/${acct}/followers`} onClick={onClose} title={intl.formatNumber(account.get('followers_count'))}>
|
||||
<strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('followers_count'))}</strong>
|
||||
<span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.followers)}</span>
|
||||
</NavLink>
|
||||
<NavLink className='sidebar-menu-profile-stat' to={`/${acct}/following`} onClick={onClose} title={intl.formatNumber(account.get('following_count'))}>
|
||||
<strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('following_count'))}</strong>
|
||||
<span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.follows)}</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='sidebar-menu__section sidebar-menu__section--borderless'>
|
||||
<NavLink className='sidebar-menu-item' to={`/${acct}`} onClick={onClose}>
|
||||
<Icon id='user' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.profile)}</span>
|
||||
</NavLink>
|
||||
<NavLink className='sidebar-menu-item' to='/lists' onClick={onClose}>
|
||||
<Icon id='list' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.lists)}</span>
|
||||
</NavLink>
|
||||
<a className='sidebar-menu-item' href='https://apps.gab.com'>
|
||||
<Icon id='th' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.apps)}</span>
|
||||
</a>
|
||||
<a className='sidebar-menu-item' href='https://blog.gab.com'>
|
||||
<Icon id='align-left' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.news)}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='sidebar-menu__section'>
|
||||
<NavLink className='sidebar-menu-item' to='/follow_requests' onClick={onClose}>
|
||||
<Icon id='user-plus' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.follow_requests)}</span>
|
||||
</NavLink>
|
||||
<NavLink className='sidebar-menu-item' to='/blocks' onClick={onClose}>
|
||||
<Icon id='ban' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.blocks)}</span>
|
||||
</NavLink>
|
||||
<NavLink className='sidebar-menu-item' to='/domain_blocks' onClick={onClose}>
|
||||
<Icon id='ban' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.domain_blocks)}</span>
|
||||
</NavLink>
|
||||
<NavLink className='sidebar-menu-item' to='/mutes' onClick={onClose}>
|
||||
<Icon id='times-circle' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.mutes)}</span>
|
||||
</NavLink>
|
||||
<a className='sidebar-menu-item' href='/filters'>
|
||||
<Icon id='filter' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.filters)}</span>
|
||||
</a>
|
||||
<a className='sidebar-menu-item' href='/settings/preferences'>
|
||||
<Icon id='cog' />
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.preferences)}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='sidebar-menu__section'>
|
||||
<a className='sidebar-menu-item' href='/auth/sign_out' data-method='delete'>
|
||||
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.logout)}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ import SearchContainer from 'gabsocial/features/compose/containers/search_contai
|
|||
import Avatar from '../../../components/avatar';
|
||||
import ActionBar from 'gabsocial/features/compose/components/action_bar';
|
||||
import { openModal } from '../../../actions/modal';
|
||||
import { openSidebar } from '../../../actions/sidebar';
|
||||
|
||||
export const privateLinks = [
|
||||
<NavLink key='pr0' className='tabs-bar__link--logo' to='/home#' data-preview-title-id='column.home' style={{ padding: '0' }}>
|
||||
|
@ -60,6 +61,7 @@ class TabsBar extends React.PureComponent {
|
|||
intl: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
onOpenCompose: PropTypes.func,
|
||||
onOpenSidebar: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
|
@ -182,6 +184,7 @@ class TabsBar extends React.PureComponent {
|
|||
<div className='flex'>
|
||||
<div className='tabs-bar__profile'>
|
||||
<Avatar account={account} />
|
||||
<button className='tabs-bar__sidebar-btn' onClick={onOpenSidebar}></button>
|
||||
<ActionBar account={account} size={34} />
|
||||
</div>
|
||||
<span className='tabs-bar__page-name'>{pathTitle}</span>
|
||||
|
@ -218,6 +221,9 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
onOpenCompose() {
|
||||
dispatch(openModal('COMPOSE'));
|
||||
},
|
||||
onOpenSidebar() {
|
||||
dispatch(openSidebar());
|
||||
},
|
||||
});
|
||||
|
||||
export default injectIntl(
|
||||
|
|
|
@ -30,6 +30,7 @@ import GroupPage from 'gabsocial/pages/group_page';
|
|||
import SearchPage from 'gabsocial/pages/search_page';
|
||||
import HomePage from 'gabsocial/pages/home_page';
|
||||
import GroupSidebarPanel from '../groups/sidebar_panel';
|
||||
import SidebarMenu from '../../components/sidebar_menu';
|
||||
|
||||
import {
|
||||
Status,
|
||||
|
@ -539,6 +540,7 @@ class UI extends React.PureComponent {
|
|||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
<SidebarMenu />
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
|
|
|
@ -36,6 +36,7 @@ import groups from './groups';
|
|||
import group_relationships from './group_relationships';
|
||||
import group_lists from './group_lists';
|
||||
import group_editor from './group_editor';
|
||||
import sidebar from './sidebar';
|
||||
|
||||
const reducers = {
|
||||
dropdown_menu,
|
||||
|
@ -75,6 +76,7 @@ const reducers = {
|
|||
group_relationships,
|
||||
group_lists,
|
||||
group_editor,
|
||||
sidebar,
|
||||
};
|
||||
|
||||
export default combineReducers(reducers);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { SIDEBAR_OPEN, SIDEBAR_CLOSE } from '../actions/sidebar';
|
||||
|
||||
export default function sidebar(state={}, action) {
|
||||
switch(action.type) {
|
||||
case SIDEBAR_OPEN:
|
||||
return { sidebarOpen: true };
|
||||
case SIDEBAR_CLOSE:
|
||||
return { sidebarOpen: false };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -31,6 +31,7 @@
|
|||
@import 'gabsocial/components/group-accounts';
|
||||
@import 'gabsocial/components/group-form';
|
||||
@import 'gabsocial/components/group-sidebar-panel';
|
||||
@import 'gabsocial/components/sidebar-menu';
|
||||
|
||||
@import 'gabsocial/polls';
|
||||
@import 'gabsocial/introduction';
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
.sidebar-menu {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
flex-direction: column;
|
||||
width: 275px;
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
transform: translateX(-275px);
|
||||
transition: all 0.15s linear;
|
||||
z-index: 10001;
|
||||
|
||||
&__root {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10000;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s linear;
|
||||
transition-delay: 0.1s;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex: 1 1;
|
||||
flex-direction: column;
|
||||
padding-bottom: 40px;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
&__section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 4px 0;
|
||||
border-top: 1px solid #ddd;
|
||||
|
||||
&--borderless {
|
||||
margin: 0;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu__root--visible {
|
||||
display: block;
|
||||
|
||||
.sidebar-menu {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.sidebar-menu__wrapper {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu-header {
|
||||
display: flex;
|
||||
height: 46px;
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
|
||||
&__title {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu-profile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
|
||||
&__avatar {
|
||||
display: block;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
|
||||
.display-name__account {
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__stats {
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu-profile-stat {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-left: 18px;
|
||||
}
|
||||
|
||||
&__value {
|
||||
display: flex;
|
||||
margin-right: 3px;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-menu-item {
|
||||
display: flex;
|
||||
padding: 16px 18px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($gab-brand-default, 0.1);
|
||||
|
||||
.fa {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&__title {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue