Change icons in navigation panel to be filled when active in web UI (#29537)
parent
4a6ddbc9c0
commit
16c856729b
|
@ -1,27 +1,30 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { useRouteMatch, NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
const ColumnLink = ({ icon, iconComponent, text, to, href, method, badge, transparent, ...other }) => {
|
const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, ...other }) => {
|
||||||
|
const match = useRouteMatch(to);
|
||||||
const className = classNames('column-link', { 'column-link--transparent': transparent });
|
const className = classNames('column-link', { 'column-link--transparent': transparent });
|
||||||
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
|
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
|
||||||
const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon;
|
const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon;
|
||||||
|
const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement);
|
||||||
|
const active = match?.isExact;
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
return (
|
return (
|
||||||
<a href={href} className={className} data-method={method} title={text} {...other}>
|
<a href={href} className={className} data-method={method} title={text} {...other}>
|
||||||
{iconElement}
|
{active ? activeIconElement : iconElement}
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
{badgeElement}
|
{badgeElement}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<NavLink to={to} className={className} title={text} {...other}>
|
<NavLink to={to} className={className} title={text} exact {...other}>
|
||||||
{iconElement}
|
{active ? activeIconElement : iconElement}
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
{badgeElement}
|
{badgeElement}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
@ -32,6 +35,8 @@ const ColumnLink = ({ icon, iconComponent, text, to, href, method, badge, transp
|
||||||
ColumnLink.propTypes = {
|
ColumnLink.propTypes = {
|
||||||
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
||||||
iconComponent: PropTypes.func,
|
iconComponent: PropTypes.func,
|
||||||
|
activeIcon: PropTypes.node,
|
||||||
|
activeIconComponent: PropTypes.func,
|
||||||
text: PropTypes.string.isRequired,
|
text: PropTypes.string.isRequired,
|
||||||
to: PropTypes.string,
|
to: PropTypes.string,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Component } from 'react';
|
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
|
||||||
|
|
||||||
import { List as ImmutableList } from 'immutable';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
|
||||||
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
|
||||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
|
||||||
import ColumnLink from 'mastodon/features/ui/components/column_link';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
text: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
|
|
||||||
});
|
|
||||||
|
|
||||||
class FollowRequestsColumnLink extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
count: PropTypes.number.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
|
|
||||||
dispatch(fetchFollowRequests());
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { count, intl } = this.props;
|
|
||||||
|
|
||||||
if (count === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ColumnLink
|
|
||||||
transparent
|
|
||||||
to='/follow_requests'
|
|
||||||
icon={<IconWithBadge className='column-link__icon' id='user-plus' icon={PersonAddIcon} count={count} />}
|
|
||||||
text={intl.formatMessage(messages.text)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps)(FollowRequestsColumnLink));
|
|
|
@ -1,10 +1,9 @@
|
||||||
import PropTypes from 'prop-types';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
|
import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react';
|
||||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||||
import { fetchLists } from 'mastodon/actions/lists';
|
import { fetchLists } from 'mastodon/actions/lists';
|
||||||
|
|
||||||
|
@ -18,40 +17,25 @@ const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
||||||
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))).take(4);
|
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))).take(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
export const ListPanel = () => {
|
||||||
lists: getOrderedLists(state),
|
const dispatch = useDispatch();
|
||||||
});
|
const lists = useSelector(state => getOrderedLists(state));
|
||||||
|
|
||||||
class ListPanel extends ImmutablePureComponent {
|
useEffect(() => {
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
lists: ImmutablePropTypes.list,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
dispatch(fetchLists());
|
dispatch(fetchLists());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
if (!lists || lists.isEmpty()) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
return (
|
||||||
const { lists } = this.props;
|
<div className='list-panel'>
|
||||||
|
<hr />
|
||||||
|
|
||||||
if (!lists || lists.isEmpty()) {
|
{lists.map(list => (
|
||||||
return null;
|
<ColumnLink icon='list-ul' key={list.get('id')} iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={list.get('title')} to={`/lists/${list.get('id')}`} transparent />
|
||||||
}
|
))}
|
||||||
|
</div>
|
||||||
return (
|
);
|
||||||
<div className='list-panel'>
|
};
|
||||||
<hr />
|
|
||||||
|
|
||||||
{lists.map(list => (
|
|
||||||
<ColumnLink icon='list-ul' iconComponent={ListAltIcon} key={list.get('id')} strict text={list.get('title')} to={`/lists/${list.get('id')}`} transparent />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(ListPanel);
|
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Component } from 'react';
|
import { Component, useEffect } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
|
||||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||||
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
|
||||||
|
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
|
||||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
|
import HomeIcon from '@/material-icons/400-24px/home.svg?react';
|
||||||
|
import ListAltActiveIcon from '@/material-icons/400-24px/list_alt-fill.svg?react';
|
||||||
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
|
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
||||||
|
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
||||||
|
import PersonAddActiveIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
|
||||||
|
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||||
import SettingsIcon from '@/material-icons/400-24px/settings-fill.svg?react';
|
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||||
|
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
|
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
||||||
|
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||||
import { WordmarkLogo } from 'mastodon/components/logo';
|
import { WordmarkLogo } from 'mastodon/components/logo';
|
||||||
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
import { NavigationPortal } from 'mastodon/components/navigation_portal';
|
||||||
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
|
||||||
|
@ -22,9 +35,7 @@ import { transientSingleColumn } from 'mastodon/is_mobile';
|
||||||
|
|
||||||
import ColumnLink from './column_link';
|
import ColumnLink from './column_link';
|
||||||
import DisabledAccountBanner from './disabled_account_banner';
|
import DisabledAccountBanner from './disabled_account_banner';
|
||||||
import FollowRequestsColumnLink from './follow_requests_column_link';
|
import { ListPanel } from './list_panel';
|
||||||
import ListPanel from './list_panel';
|
|
||||||
import NotificationsCounterIcon from './notifications_counter_icon';
|
|
||||||
import SignInBanner from './sign_in_banner';
|
import SignInBanner from './sign_in_banner';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -42,8 +53,48 @@ const messages = defineMessages({
|
||||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
||||||
advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' },
|
advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' },
|
||||||
openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' },
|
openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' },
|
||||||
|
followRequests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const NotificationsLink = () => {
|
||||||
|
const count = useSelector(state => state.getIn(['notifications', 'unread']));
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColumnLink
|
||||||
|
transparent
|
||||||
|
to='/notifications'
|
||||||
|
icon={<IconWithBadge icon={NotificationsIcon} count={count} className='column-link__icon' />}
|
||||||
|
activeIcon={<IconWithBadge icon={NotificationsActiveIcon} count={count} className='column-link__icon' />}
|
||||||
|
text={intl.formatMessage(messages.notifications)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FollowRequestsLink = () => {
|
||||||
|
const count = useSelector(state => state.getIn(['user_lists', 'follow_requests', 'items'])?.size ?? 0);
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchFollowRequests());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
if (count === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColumnLink
|
||||||
|
transparent
|
||||||
|
to='/follow_requests'
|
||||||
|
icon={<IconWithBadge icon={PersonAddIcon} count={count} className='column-link__icon' />}
|
||||||
|
activeIcon={<IconWithBadge icon={PersonAddActiveIcon} count={count} className='column-link__icon' />}
|
||||||
|
text={intl.formatMessage(messages.followRequests)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
class NavigationPanel extends Component {
|
class NavigationPanel extends Component {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -87,9 +138,9 @@ class NavigationPanel extends Component {
|
||||||
|
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<>
|
<>
|
||||||
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home)} />
|
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
|
||||||
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
|
<NotificationsLink />
|
||||||
<FollowRequestsColumnLink />
|
<FollowRequestsLink />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -113,9 +164,9 @@ class NavigationPanel extends Component {
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<>
|
<>
|
||||||
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
|
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
|
||||||
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} />
|
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
|
||||||
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} />
|
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
|
||||||
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} />
|
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
|
||||||
|
|
||||||
<ListPanel />
|
<ListPanel />
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
|
|
||||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
count: state.getIn(['notifications', 'unread']),
|
|
||||||
id: 'bell',
|
|
||||||
icon: NotificationsIcon,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(IconWithBadge);
|
|
Loading…
Reference in New Issue