183 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import PropTypes from 'prop-types';
 | |
| import { PureComponent } from 'react';
 | |
| 
 | |
| import { defineMessages, injectIntl } from 'react-intl';
 | |
| 
 | |
| import { Helmet } from 'react-helmet';
 | |
| 
 | |
| import { List as ImmutableList } from 'immutable';
 | |
| import ImmutablePropTypes from 'react-immutable-proptypes';
 | |
| import { connect } from 'react-redux';
 | |
| 
 | |
| import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
 | |
| 
 | |
| import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'mastodon/actions/columns';
 | |
| import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
 | |
| import Column from 'mastodon/components/column';
 | |
| import ColumnHeader from 'mastodon/components/column_header';
 | |
| import { LoadMore } from 'mastodon/components/load_more';
 | |
| import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 | |
| import { RadioButton } from 'mastodon/components/radio_button';
 | |
| import ScrollContainer from 'mastodon/containers/scroll_container';
 | |
| 
 | |
| import AccountCard from './components/account_card';
 | |
| 
 | |
| const messages = defineMessages({
 | |
|   title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
 | |
|   recentlyActive: { id: 'directory.recently_active', defaultMessage: 'Recently active' },
 | |
|   newArrivals: { id: 'directory.new_arrivals', defaultMessage: 'New arrivals' },
 | |
|   local: { id: 'directory.local', defaultMessage: 'From {domain} only' },
 | |
|   federated: { id: 'directory.federated', defaultMessage: 'From known fediverse' },
 | |
| });
 | |
| 
 | |
| const mapStateToProps = state => ({
 | |
|   accountIds: state.getIn(['user_lists', 'directory', 'items'], ImmutableList()),
 | |
|   isLoading: state.getIn(['user_lists', 'directory', 'isLoading'], true),
 | |
|   domain: state.getIn(['meta', 'domain']),
 | |
| });
 | |
| 
 | |
| class Directory extends PureComponent {
 | |
| 
 | |
|   static propTypes = {
 | |
|     isLoading: PropTypes.bool,
 | |
|     accountIds: ImmutablePropTypes.list.isRequired,
 | |
|     dispatch: PropTypes.func.isRequired,
 | |
|     columnId: PropTypes.string,
 | |
|     intl: PropTypes.object.isRequired,
 | |
|     multiColumn: PropTypes.bool,
 | |
|     domain: PropTypes.string.isRequired,
 | |
|     params: PropTypes.shape({
 | |
|       order: PropTypes.string,
 | |
|       local: PropTypes.bool,
 | |
|     }),
 | |
|   };
 | |
| 
 | |
|   state = {
 | |
|     order: null,
 | |
|     local: null,
 | |
|   };
 | |
| 
 | |
|   handlePin = () => {
 | |
|     const { columnId, dispatch } = this.props;
 | |
| 
 | |
|     if (columnId) {
 | |
|       dispatch(removeColumn(columnId));
 | |
|     } else {
 | |
|       dispatch(addColumn('DIRECTORY', this.getParams(this.props, this.state)));
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   getParams = (props, state) => ({
 | |
|     order: state.order === null ? (props.params.order || 'active') : state.order,
 | |
|     local: state.local === null ? (props.params.local || false) : state.local,
 | |
|   });
 | |
| 
 | |
|   handleMove = dir => {
 | |
|     const { columnId, dispatch } = this.props;
 | |
|     dispatch(moveColumn(columnId, dir));
 | |
|   };
 | |
| 
 | |
|   handleHeaderClick = () => {
 | |
|     this.column.scrollTop();
 | |
|   };
 | |
| 
 | |
|   componentDidMount () {
 | |
|     const { dispatch } = this.props;
 | |
|     dispatch(fetchDirectory(this.getParams(this.props, this.state)));
 | |
|   }
 | |
| 
 | |
|   componentDidUpdate (prevProps, prevState) {
 | |
|     const { dispatch } = this.props;
 | |
|     const paramsOld = this.getParams(prevProps, prevState);
 | |
|     const paramsNew = this.getParams(this.props, this.state);
 | |
| 
 | |
|     if (paramsOld.order !== paramsNew.order || paramsOld.local !== paramsNew.local) {
 | |
|       dispatch(fetchDirectory(paramsNew));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   setRef = c => {
 | |
|     this.column = c;
 | |
|   };
 | |
| 
 | |
|   handleChangeOrder = e => {
 | |
|     const { dispatch, columnId } = this.props;
 | |
| 
 | |
|     if (columnId) {
 | |
|       dispatch(changeColumnParams(columnId, ['order'], e.target.value));
 | |
|     } else {
 | |
|       this.setState({ order: e.target.value });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   handleChangeLocal = e => {
 | |
|     const { dispatch, columnId } = this.props;
 | |
| 
 | |
|     if (columnId) {
 | |
|       dispatch(changeColumnParams(columnId, ['local'], e.target.value === '1'));
 | |
|     } else {
 | |
|       this.setState({ local: e.target.value === '1' });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   handleLoadMore = () => {
 | |
|     const { dispatch } = this.props;
 | |
|     dispatch(expandDirectory(this.getParams(this.props, this.state)));
 | |
|   };
 | |
| 
 | |
|   render () {
 | |
|     const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props;
 | |
|     const { order, local }  = this.getParams(this.props, this.state);
 | |
|     const pinned = !!columnId;
 | |
| 
 | |
|     const scrollableArea = (
 | |
|       <div className='scrollable'>
 | |
|         <div className='filter-form'>
 | |
|           <div className='filter-form__column' role='group'>
 | |
|             <RadioButton name='order' value='active' label={intl.formatMessage(messages.recentlyActive)} checked={order === 'active'} onChange={this.handleChangeOrder} />
 | |
|             <RadioButton name='order' value='new' label={intl.formatMessage(messages.newArrivals)} checked={order === 'new'} onChange={this.handleChangeOrder} />
 | |
|           </div>
 | |
| 
 | |
|           <div className='filter-form__column' role='group'>
 | |
|             <RadioButton name='local' value='1' label={intl.formatMessage(messages.local, { domain })} checked={local} onChange={this.handleChangeLocal} />
 | |
|             <RadioButton name='local' value='0' label={intl.formatMessage(messages.federated)} checked={!local} onChange={this.handleChangeLocal} />
 | |
|           </div>
 | |
|         </div>
 | |
| 
 | |
|         <div className='directory__list'>
 | |
|           {isLoading ? <LoadingIndicator /> : accountIds.map(accountId => (
 | |
|             <AccountCard id={accountId} key={accountId} />
 | |
|           ))}
 | |
|         </div>
 | |
| 
 | |
|         <LoadMore onClick={this.handleLoadMore} visible={!isLoading} />
 | |
|       </div>
 | |
|     );
 | |
| 
 | |
|     return (
 | |
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
 | |
|         <ColumnHeader
 | |
|           icon='address-book-o'
 | |
|           iconComponent={PeopleIcon}
 | |
|           title={intl.formatMessage(messages.title)}
 | |
|           onPin={this.handlePin}
 | |
|           onMove={this.handleMove}
 | |
|           onClick={this.handleHeaderClick}
 | |
|           pinned={pinned}
 | |
|           multiColumn={multiColumn}
 | |
|         />
 | |
| 
 | |
|         {multiColumn && !pinned ? <ScrollContainer scrollKey='directory'>{scrollableArea}</ScrollContainer> : scrollableArea}
 | |
| 
 | |
|         <Helmet>
 | |
|           <title>{intl.formatMessage(messages.title)}</title>
 | |
|           <meta name='robots' content='noindex' />
 | |
|         </Helmet>
 | |
|       </Column>
 | |
|     );
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| export default connect(mapStateToProps)(injectIntl(Directory));
 |