diff --git a/app/soapbox/components/primary_navigation.js b/app/soapbox/components/primary_navigation.js new file mode 100644 index 000000000..a1c3b5362 --- /dev/null +++ b/app/soapbox/components/primary_navigation.js @@ -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 ( +
+

+ + + + + + {account && + + + } + + {(features.chats && account) && + + + } + + {(account && isStaff(account)) && + + + } + + {/* + + {federating ? siteTitle : } + */} + + {/* federating && + + + */} +

+
+ ); + } + +} diff --git a/app/soapbox/components/sub_navigation.js b/app/soapbox/components/sub_navigation.js new file mode 100644 index 000000000..28a92e5ff --- /dev/null +++ b/app/soapbox/components/sub_navigation.js @@ -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 ( +
+ +
+ {intl.formatMessage(message)} +
+
+ ); + } + +} diff --git a/app/soapbox/components/thumb_navigation.js b/app/soapbox/components/thumb_navigation.js new file mode 100644 index 000000000..e8b1f3bb9 --- /dev/null +++ b/app/soapbox/components/thumb_navigation.js @@ -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 ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); + } + +} diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index af09692bb..a897d1602 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -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 {
+ {children} @@ -668,6 +671,7 @@ class UI extends React.PureComponent { )} +
); diff --git a/app/soapbox/pages/home_page.js b/app/soapbox/pages/home_page.js index b39403c2f..bebfe40c2 100644 --- a/app/soapbox/pages/home_page.js +++ b/app/soapbox/pages/home_page.js @@ -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 {
- - {showFundingPanel && } - {showCryptoDonatePanel && ( - - {Component => } - - )} +
@@ -86,10 +81,17 @@ class HomePage extends ImmutablePureComponent {
+ {/* */} {showTrendsPanel && } {showWhoToFollowPanel && } {me ? : } + {showFundingPanel && } + {showCryptoDonatePanel && ( + + {Component => } + + )}
diff --git a/app/styles/application.scss b/app/styles/application.scss index 9b9bbff36..844e88a4c 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -28,6 +28,7 @@ @import 'dyslexic'; @import 'demetricator'; @import 'chats'; +@import 'navigation'; // COMPONENTS @import 'components/buttons'; diff --git a/app/styles/navigation.scss b/app/styles/navigation.scss new file mode 100644 index 000000000..f02d14a44 --- /dev/null +++ b/app/styles/navigation.scss @@ -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; + } +} diff --git a/app/styles/ui.scss b/app/styles/ui.scss index 8a111f31e..760122aae 100644 --- a/app/styles/ui.scss +++ b/app/styles/ui.scss @@ -358,7 +358,7 @@ z-index: 1000; display: none; position: fixed; - bottom: 14px; + bottom: 64px; right: 14px; width: 61px; height: 61px;