Add mobile account switcher

This commit is contained in:
Alex Gleason 2021-03-26 22:34:30 -05:00
parent c5778472f5
commit 16ce14e403
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
2 changed files with 95 additions and 18 deletions

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { throttle } from 'lodash';
import { Link, NavLink } from 'react-router-dom'; import { Link, NavLink } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
@ -14,8 +15,10 @@ import { closeSidebar } from '../actions/sidebar';
import { shortNumberFormat } from '../utils/numbers'; import { shortNumberFormat } from '../utils/numbers';
import { isStaff } from '../utils/accounts'; import { isStaff } from '../utils/accounts';
import { makeGetAccount } from '../selectors'; import { makeGetAccount } from '../selectors';
import { logOut } from 'soapbox/actions/auth'; import { logOut, switchAccount } from 'soapbox/actions/auth';
import ThemeToggle from '../features/ui/components/theme_toggle_container'; import ThemeToggle from '../features/ui/components/theme_toggle_container';
import { fetchOwnAccounts } from 'soapbox/actions/auth';
import { List as ImmutableList } from 'immutable';
const messages = defineMessages({ const messages = defineMessages({
followers: { id: 'account.followers', defaultMessage: 'Followers' }, followers: { id: 'account.followers', defaultMessage: 'Followers' },
@ -38,17 +41,30 @@ const messages = defineMessages({
news: { id: 'tabs_bar.news', defaultMessage: 'News' }, news: { id: 'tabs_bar.news', defaultMessage: 'News' },
donate: { id: 'donate', defaultMessage: 'Donate' }, donate: { id: 'donate', defaultMessage: 'Donate' },
info: { id: 'column.info', defaultMessage: 'Server information' }, info: { id: 'column.info', defaultMessage: 'Server information' },
add_account: { id: 'profile_dropdown.add_account', defaultMessage: 'Add an existing account' },
}); });
const mapStateToProps = state => { const mapStateToProps = state => {
const me = state.get('me'); const me = state.get('me');
const getAccount = makeGetAccount(); const getAccount = makeGetAccount();
const otherAccounts =
state
.getIn(['auth', 'users'])
.keySeq()
.reduce((list, id) => {
if (id === me) return list;
const account = state.getIn(['accounts', id]);
return account ? list.push(account) : list;
}, ImmutableList());
return { return {
account: getAccount(state, me), account: getAccount(state, me),
sidebarOpen: state.get('sidebar').sidebarOpen, sidebarOpen: state.get('sidebar').sidebarOpen,
donateUrl: state.getIn(['patron', 'instance', 'url']), donateUrl: state.getIn(['patron', 'instance', 'url']),
isStaff: isStaff(state.getIn(['accounts', me])), isStaff: isStaff(state.getIn(['accounts', me])),
otherAccounts,
}; };
}; };
@ -60,6 +76,12 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(logOut()); dispatch(logOut());
e.preventDefault(); e.preventDefault();
}, },
fetchOwnAccounts() {
dispatch(fetchOwnAccounts());
},
switchAccount(account) {
dispatch(switchAccount(account.get('id')));
},
}); });
export default @connect(mapStateToProps, mapDispatchToProps) export default @connect(mapStateToProps, mapDispatchToProps)
@ -78,8 +100,52 @@ class SidebarMenu extends ImmutablePureComponent {
isStaff: false, isStaff: false,
} }
state = {
switcher: false,
}
handleSwitchAccount = account => {
return e => {
this.props.switchAccount(account);
e.preventDefault();
};
}
handleSwitcherClick = e => {
this.setState({ switcher: !this.state.switcher });
e.preventDefault();
}
fetchOwnAccounts = throttle(() => {
this.props.fetchOwnAccounts();
}, 2000);
componentDidMount() {
this.fetchOwnAccounts();
}
componentDidUpdate() {
this.fetchOwnAccounts();
}
renderAccount = account => {
return (
<a href='#' className='sidebar-account' onClick={this.handleSwitchAccount(account)} key={account.get('id')}>
<div className='account'>
<div className='account__wrapper'>
<div className='account__display-name' title={account.get('acct')} href={`/@${account.get('acct')}`} to={`/@${account.get('acct')}`}>
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
<DisplayName account={account} />
</div>
</div>
</div>
</a>
);
}
render() { render() {
const { sidebarOpen, onClose, intl, account, onClickLogOut, donateUrl, isStaff } = this.props; const { sidebarOpen, onClose, intl, account, onClickLogOut, donateUrl, isStaff, otherAccounts } = this.props;
const { switcher } = this.state;
if (!account) return null; if (!account) return null;
const acct = account.get('acct'); const acct = account.get('acct');
@ -105,24 +171,22 @@ class SidebarMenu extends ImmutablePureComponent {
<Avatar account={account} /> <Avatar account={account} />
</Link> </Link>
</div> </div>
<div className='sidebar-menu-profile__name'> <a href='#' className='sidebar-menu-profile__name' onClick={this.handleSwitcherClick}>
<DisplayName account={account} /> <DisplayName account={account} />
</div> <Icon id={switcher ? 'caret-up' : 'caret-down'} />
</a>
<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>
<div className='sidebar-menu__section sidebar-menu__section--borderless'> {switcher && <div className='sidebar-menu__section'>
{otherAccounts.map(account => this.renderAccount(account))}
<NavLink className='sidebar-menu-item' to='/auth/sign_in' onClick={onClose}>
<Icon id='plus' />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.add_account)}</span>
</NavLink>
</div>}
<div className='sidebar-menu__section'>
<div className='sidebar-menu-item theme-toggle'> <div className='sidebar-menu-item theme-toggle'>
<ThemeToggle showLabel /> <ThemeToggle showLabel />
</div> </div>

View file

@ -98,14 +98,23 @@
} }
&__name { &__name {
display: block; display: flex;
margin-top: 10px; margin-top: 10px;
color: var(--primary-text-color);
text-decoration: none;
align-items: center;
.display-name__account { .display-name__account {
display: block; display: block;
margin-top: 2px; margin-top: 2px;
color: var(--primary-text-color--faint); color: var(--primary-text-color--faint);
} }
i.fa-caret-up,
i.fa-caret-down {
margin-left: auto;
padding-left: 10px;
}
} }
&__stats { &__stats {
@ -140,6 +149,10 @@
} }
} }
.sidebar-account {
text-decoration: none;
}
.sidebar-menu-item { .sidebar-menu-item {
display: flex; display: flex;
padding: 16px 18px; padding: 16px 18px;