diff options
Diffstat (limited to 'app/javascript/flavours/glitch/features/follow_requests')
3 files changed, 175 insertions, 0 deletions
diff --git a/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js new file mode 100644 index 000000000..cbe7a1032 --- /dev/null +++ b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Permalink from 'flavours/glitch/components/permalink'; +import Avatar from 'flavours/glitch/components/avatar'; +import DisplayName from 'flavours/glitch/components/display_name'; +import IconButton from 'flavours/glitch/components/icon_button'; +import { defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + +const messages = defineMessages({ + authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, + reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }, +}); + +export default @injectIntl +class AccountAuthorize extends ImmutablePureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + onAuthorize: PropTypes.func.isRequired, + onReject: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + render () { + const { intl, account, onAuthorize, onReject } = this.props; + const content = { __html: account.get('note_emojified') }; + + return ( + <div className='account-authorize__wrapper'> + <div className='account-authorize'> + <Permalink href={account.get('url')} to={`/@${account.get('acct')}`} className='detailed-status__display-name'> + <div className='account-authorize__avatar'><Avatar account={account} size={48} /></div> + <DisplayName account={account} /> + </Permalink> + + <div className='account__header__content translate' dangerouslySetInnerHTML={content} /> + </div> + + <div className='account--panel'> + <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> + <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> + </div> + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/follow_requests/containers/account_authorize_container.js b/app/javascript/flavours/glitch/features/follow_requests/containers/account_authorize_container.js new file mode 100644 index 000000000..693e98e8c --- /dev/null +++ b/app/javascript/flavours/glitch/features/follow_requests/containers/account_authorize_container.js @@ -0,0 +1,26 @@ +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import AccountAuthorize from '../components/account_authorize'; +import { authorizeFollowRequest, rejectFollowRequest } from 'flavours/glitch/actions/accounts'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, props) => ({ + account: getAccount(state, props.id), + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { id }) => ({ + onAuthorize () { + dispatch(authorizeFollowRequest(id)); + }, + + onReject () { + dispatch(rejectFollowRequest(id)); + }, +}); + +export default connect(makeMapStateToProps, mapDispatchToProps)(AccountAuthorize); diff --git a/app/javascript/flavours/glitch/features/follow_requests/index.js b/app/javascript/flavours/glitch/features/follow_requests/index.js new file mode 100644 index 000000000..7b35e3ec9 --- /dev/null +++ b/app/javascript/flavours/glitch/features/follow_requests/index.js @@ -0,0 +1,100 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { debounce } from 'lodash'; +import LoadingIndicator from 'flavours/glitch/components/loading_indicator'; +import Column from 'flavours/glitch/features/ui/components/column'; +import ColumnBackButtonSlim from 'flavours/glitch/components/column_back_button_slim'; +import AccountAuthorizeContainer from './containers/account_authorize_container'; +import { fetchFollowRequests, expandFollowRequests } from 'flavours/glitch/actions/accounts'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import ScrollableList from 'flavours/glitch/components/scrollable_list'; +import { me } from 'flavours/glitch/initial_state'; +import { Helmet } from 'react-helmet'; + +const messages = defineMessages({ + heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' }, +}); + +const mapStateToProps = state => ({ + accountIds: state.getIn(['user_lists', 'follow_requests', 'items']), + isLoading: state.getIn(['user_lists', 'follow_requests', 'isLoading'], true), + hasMore: !!state.getIn(['user_lists', 'follow_requests', 'next']), + locked: !!state.getIn(['accounts', me, 'locked']), + domain: state.getIn(['meta', 'domain']), +}); + +export default @connect(mapStateToProps) +@injectIntl +class FollowRequests extends ImmutablePureComponent { + + static propTypes = { + params: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, + accountIds: ImmutablePropTypes.list, + locked: PropTypes.bool, + domain: PropTypes.string, + intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, + }; + + componentWillMount () { + this.props.dispatch(fetchFollowRequests()); + } + + handleLoadMore = debounce(() => { + this.props.dispatch(expandFollowRequests()); + }, 300, { leading: true }); + + render () { + const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props; + + if (!accountIds) { + return ( + <Column name='follow-requests'> + <LoadingIndicator /> + </Column> + ); + } + + const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />; + const unlockedPrependMessage = locked ? null : ( + <div className='follow_requests-unlocked_explanation'> + <FormattedMessage + id='follow_requests.unlocked_explanation' + defaultMessage='Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.' + values={{ domain: domain }} + /> + </div> + ); + + return ( + <Column bindToDocument={!multiColumn} name='follow-requests' icon='user-plus' heading={intl.formatMessage(messages.heading)}> + <ColumnBackButtonSlim /> + + <ScrollableList + scrollKey='follow_requests' + onLoadMore={this.handleLoadMore} + hasMore={hasMore} + isLoading={isLoading} + emptyMessage={emptyMessage} + bindToDocument={!multiColumn} + prepend={unlockedPrependMessage} + > + {accountIds.map(id => + <AccountAuthorizeContainer key={id} id={id} />, + )} + </ScrollableList> + + <Helmet> + <meta name='robots' content='noindex' /> + </Helmet> + </Column> + ); + } + +} |