From 0be86d6ec505f5c567137a4d535aff226b133027 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Wed, 14 Aug 2019 23:04:03 -0400 Subject: [PATCH] Added sidebar menu feature --- app/javascript/gabsocial/actions/sidebar.js | 14 ++ .../gabsocial/components/sidebar_menu.js | 161 +++++++++++++++++ .../features/ui/components/tabs_bar.js | 6 + app/javascript/gabsocial/features/ui/index.js | 2 + app/javascript/gabsocial/reducers/index.js | 2 + app/javascript/gabsocial/reducers/sidebar.js | 12 ++ app/javascript/styles/application.scss | 1 + .../gabsocial/components/sidebar-menu.scss | 171 ++++++++++++++++++ 8 files changed, 369 insertions(+) create mode 100644 app/javascript/gabsocial/actions/sidebar.js create mode 100644 app/javascript/gabsocial/components/sidebar_menu.js create mode 100644 app/javascript/gabsocial/reducers/sidebar.js create mode 100644 app/javascript/styles/gabsocial/components/sidebar-menu.scss diff --git a/app/javascript/gabsocial/actions/sidebar.js b/app/javascript/gabsocial/actions/sidebar.js new file mode 100644 index 00000000..bd646796 --- /dev/null +++ b/app/javascript/gabsocial/actions/sidebar.js @@ -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, + }; +}; diff --git a/app/javascript/gabsocial/components/sidebar_menu.js b/app/javascript/gabsocial/components/sidebar_menu.js new file mode 100644 index 00000000..9fc45262 --- /dev/null +++ b/app/javascript/gabsocial/components/sidebar_menu.js @@ -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 ( +
+
+
+ +
+ Account Info + +
+ +
+ +
+
+ + + +
+
+ +
+ +
+ + {shortNumberFormat(account.get('followers_count'))} + {intl.formatMessage(messages.followers)} + + + {shortNumberFormat(account.get('following_count'))} + {intl.formatMessage(messages.follows)} + +
+ +
+ +
+ + + {intl.formatMessage(messages.profile)} + + + + {intl.formatMessage(messages.lists)} + + + + {intl.formatMessage(messages.apps)} + + + + {intl.formatMessage(messages.news)} + +
+ +
+ + + {intl.formatMessage(messages.follow_requests)} + + + + {intl.formatMessage(messages.blocks)} + + + + {intl.formatMessage(messages.domain_blocks)} + + + + {intl.formatMessage(messages.mutes)} + + + + {intl.formatMessage(messages.filters)} + + + + {intl.formatMessage(messages.preferences)} + +
+ +
+ + {intl.formatMessage(messages.logout)} + +
+ +
+
+
+ ); + } + +} diff --git a/app/javascript/gabsocial/features/ui/components/tabs_bar.js b/app/javascript/gabsocial/features/ui/components/tabs_bar.js index ee4bce0b..f94f1442 100644 --- a/app/javascript/gabsocial/features/ui/components/tabs_bar.js +++ b/app/javascript/gabsocial/features/ui/components/tabs_bar.js @@ -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 = [ @@ -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 {
+
{pathTitle} @@ -218,6 +221,9 @@ const mapDispatchToProps = (dispatch) => ({ onOpenCompose() { dispatch(openModal('COMPOSE')); }, + onOpenSidebar() { + dispatch(openSidebar()); + }, }); export default injectIntl( diff --git a/app/javascript/gabsocial/features/ui/index.js b/app/javascript/gabsocial/features/ui/index.js index 7aa604b1..43c18ede 100644 --- a/app/javascript/gabsocial/features/ui/index.js +++ b/app/javascript/gabsocial/features/ui/index.js @@ -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 { +
); diff --git a/app/javascript/gabsocial/reducers/index.js b/app/javascript/gabsocial/reducers/index.js index 06370d27..82921f51 100644 --- a/app/javascript/gabsocial/reducers/index.js +++ b/app/javascript/gabsocial/reducers/index.js @@ -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); diff --git a/app/javascript/gabsocial/reducers/sidebar.js b/app/javascript/gabsocial/reducers/sidebar.js new file mode 100644 index 00000000..92d14839 --- /dev/null +++ b/app/javascript/gabsocial/reducers/sidebar.js @@ -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; + } +}; diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index ab469abd..4cb2e478 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -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'; diff --git a/app/javascript/styles/gabsocial/components/sidebar-menu.scss b/app/javascript/styles/gabsocial/components/sidebar-menu.scss new file mode 100644 index 00000000..36d80b77 --- /dev/null +++ b/app/javascript/styles/gabsocial/components/sidebar-menu.scss @@ -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; + } + } +} \ No newline at end of file