2017-06-08_509bba0/509bba0_unpacked_with_node_modules/discord_app/components/guild_settings/GuildSettingsMembers.js
2022-07-26 10:06:20 -07:00

365 lines
11 KiB
JavaScript
Executable file

/* @flow */
import React from 'react';
import Flux from '../../lib/flux';
import i18n from '../../i18n';
import lodash from 'lodash';
import classNames from 'classnames';
import fuzzysearch from 'fuzzysearch';
import GuildSettingsStore from '../../stores/GuildSettingsStore';
import GuildMemberStore from '../../stores/GuildMemberStore';
import UserSettingsStore from '../../stores/UserSettingsStore';
import UserStore from '../../stores/UserStore';
import PermissionStore from '../../stores/PermissionStore';
import StreamerModeStore from '../../stores/StreamerModeStore';
import PopoutActionCreators from '../../actions/PopoutActionCreators';
import GuildSettingsActionCreators from '../../actions/GuildSettingsActionCreators';
import PruneGuildModalActionCreators from '../../actions/PruneGuildModalActionCreators';
import {LazyContentScroller} from '../common/StandardSidebarView';
import ContextMenu from '../common/ContextMenu';
import UserContextMenu from '../contextmenus/UserContextMenu';
import RoleList from '../common/RoleList';
import SearchableQuickSelect from '../common/SearchableQuickSelect';
import Tooltip from '../common/Tooltip';
import IconButton from '../../uikit/IconButton';
import OverflowMenuIcon from '../../uikit/icons/OverflowMenuIcon';
import BotTag from '../../uikit/BotTag';
import Avatar from '../../uikit/Avatar';
import Flex from '../../uikit/Flex';
import {FormTitle, FormTitleTags, FormText, FormTextTypes, FormDivider, FormSection} from '../../uikit/form';
import SearchBar from '../../uikit/SearchBar';
import {ContextMenuTypes, Permissions, ThemeTypes} from '../../Constants';
import './GuildSettingsMembers.styl';
import '../../styles/shared/hover_card.styl';
const ROLE_FILTER_POPOUT_ID = 'quick-roles--select';
class Member extends React.PureComponent {
_isMounted = false;
state: {
overflowShown: boolean,
};
constructor(props) {
super(props);
this.state = {
overflowShown: false,
};
(this: any).showOverflow = this.showOverflow.bind(this);
(this: any).handleOverflowClose = this.handleOverflowClose.bind(this);
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
showOverflow(e: Event) {
const {guild, user} = this.props;
this.setState({overflowShown: true});
ContextMenu.openContextMenu(
e,
props =>
<UserContextMenu
type={ContextMenuTypes.USER_GUILD_MEMBERS}
user={user}
guildId={guild.id}
channelId={guild.id}
{...props}
/>,
{
onClose: this.handleOverflowClose,
}
);
}
renderOwnerHelpIcon() {
const {user, theme, guild} = this.props;
if (user.id !== guild.ownerId) {
return null;
}
const iconSrc = theme === ThemeTypes.DARK
? require('../../images/ic_crown_dark_24px.svg')
: require('../../images/ic_crown_light_24px.svg');
return (
<Tooltip text={i18n.Messages.MEMBER_LIST_SERVER_OWNER_HELP}>
<IconButton size={IconButton.Sizes.SMALL} src={iconSrc} className="owner-help-icon" />
</Tooltip>
);
}
render() {
const {user, member, streamerMode, guild} = this.props;
const {overflowShown} = this.state;
const style = {
color: member.colorString,
};
return (
<Flex
className={classNames('guild-settings-members-member', 'ui-hover-card', {active: overflowShown})}
align={Flex.Align.CENTER}
onContextMenu={this.showOverflow}>
<Avatar className="member-avatar" src={user.getAvatarURL()} />
<Flex className="name-and-tag" direction={Flex.Direction.VERTICAL} grow={0}>
<Flex align={Flex.Align.CENTER}>
<FormText className="name" style={style}>
{member.nick || user.toString()}
</FormText>
{user.bot ? <BotTag /> : null}
{this.renderOwnerHelpIcon()}
</Flex>
{streamerMode ? null : <FormText className="tag">@{user.tag}</FormText>}
</Flex>
<Flex.Child className="role-wrapper">
<div>
<RoleList user={user} guild={guild} memberRoles={member.roles} />
</div>
</Flex.Child>
<Flex className="overflow-button" align={Flex.Align.CENTER} grow={0} onClick={this.showOverflow}>
<OverflowMenuIcon />
</Flex>
</Flex>
);
}
handleOverflowClose() {
if (this._isMounted) {
this.setState({overflowShown: false});
}
}
}
class GuildSettingsMembers extends React.PureComponent {
state: {
roleFilterQuery: string,
};
constructor(props) {
super(props);
this.state = {
roleFilterQuery: '',
};
(this: any).renderHeader = this.renderHeader.bind(this);
(this: any).renderPruneAction = this.renderPruneAction.bind(this);
(this: any).handleQueryChange = this.handleQueryChange.bind(this);
(this: any).handleQueryClear = this.handleQueryClear.bind(this);
(this: any).handlePruneClick = this.handlePruneClick.bind(this);
(this: any).handleRoleFilterQueryChange = this.handleRoleFilterQueryChange.bind(this);
(this: any).handleRoleFilterQueryClear = this.handleRoleFilterQueryClear.bind(this);
}
componentDidUpdate(prevProps, prevState) {
if (this.state.roleFilterQuery !== prevState.roleFilterQuery) {
PopoutActionCreators.rerender(ROLE_FILTER_POPOUT_ID);
}
}
handlePruneClick() {
PruneGuildModalActionCreators.open(this.props.guild.id);
}
renderPruneAction() {
const {canPrune} = this.props;
if (!canPrune) {
return null;
}
return (
<span>
{' — '}
<a onClick={this.handlePruneClick} href="#" className="prune-red-link">
{i18n.Messages.PRUNE}
</a>
</span>
);
}
renderHeader(filteredMembers) {
const {roleFilterQuery} = this.state;
const {guild, searchQuery, selectedRoleId} = this.props;
const value = {};
const items = lodash(guild.roles)
.sortBy(role => -role.position)
.filter(role => {
if (role.id === selectedRoleId) {
value.label = role.name;
}
return fuzzysearch(roleFilterQuery.toLowerCase(), role.name.toLowerCase());
})
.map(role => ({
value: role.id,
color: role.colorString,
children: role.name,
selected: role.id === selectedRoleId,
}))
.value();
const searchProps = {
query: roleFilterQuery,
onChange: this.handleRoleFilterQueryChange,
onClear: this.handleRoleFilterQueryClear,
placeholder: i18n.Messages.SEARCH_ROLES,
};
return (
<FormSection>
<FormTitle tag={FormTitleTags.H2}>
{i18n.Messages.GUILD_SETTINGS_MEMBERS_SERVER_MEMBERS}
</FormTitle>
<Flex className="guild-settings-members-header" align={Flex.Align.CENTER} style={{height: 20}}>
<Flex.Child>
<FormText type={FormTextTypes.DESCRIPTION} className="guild-settings-members-count">
{i18n.Messages.MEMBERS_HEADER.format({members: filteredMembers.length})}
{this.renderPruneAction()}
</FormText>
</Flex.Child>
<Flex align={Flex.Align.CENTER} grow={0}>
<SearchableQuickSelect
popoutClassName="guild-settings-members-filter-popout elevation-border-low"
popoutId={ROLE_FILTER_POPOUT_ID}
className="role-filter"
items={items}
label={i18n.Messages.GUILD_SETTINGS_MEMBERS_DISPLAY_ROLE}
value={value}
onChange={this.handleRoleChange}
searchProps={searchProps}
popoutProps={{
preventInvert: true,
position: 'bottom',
}}
/>
<SearchBar query={searchQuery} onChange={this.handleQueryChange} onClear={this.handleQueryClear} />
</Flex>
</Flex>
<FormDivider className="margin-top-20" style={{marginBottom: -1}} />
</FormSection>
);
}
makeSearchFilter() {
let {searchQuery} = this.props;
if (searchQuery == null || searchQuery.length === 0) {
return () => true;
} else {
return ({user, member}) => {
searchQuery = searchQuery.toLowerCase();
const username = user.username.toLowerCase();
const nick = member.nick != null ? member.nick.toLowerCase() : null;
return fuzzysearch(searchQuery, username) || (nick != null && fuzzysearch(searchQuery, nick));
};
}
}
makeRoleFilter() {
const {guild, selectedRoleId} = this.props;
return ({member}) => selectedRoleId === guild.id || member.roles.indexOf(selectedRoleId) !== -1;
}
renderMembers(filteredMembers) {
const {guild, currentUser, streamerMode, theme} = this.props;
return lodash(filteredMembers)
.sortBy(({comparator}) => comparator)
.map(({member, user}) =>
<Member
currentUser={currentUser}
member={member}
user={user}
guild={guild}
key={member.userId}
theme={theme}
streamerMode={streamerMode}
/>
)
.value();
}
render() {
const {members} = this.props;
const filteredMembers = lodash(members)
.map(member => {
const user = UserStore.getUser(member.userId);
return {
member,
user,
comparator: (user != null ? member.nick || user.username : '').toLowerCase(),
};
})
.filter(this.makeSearchFilter())
.filter(this.makeRoleFilter())
.value();
return (
<LazyContentScroller theme={this.props.theme} header={this.renderHeader(filteredMembers)} elementHeight={72}>
{this.renderMembers(filteredMembers)}
</LazyContentScroller>
);
}
handleQueryChange(query) {
GuildSettingsActionCreators.setSearchQuery(query);
}
handleQueryClear() {
GuildSettingsActionCreators.setSearchQuery('');
}
handleRoleChange({value}) {
GuildSettingsActionCreators.selectRole(value);
}
handleRoleFilterQueryChange(roleFilterQuery) {
this.setState({roleFilterQuery});
}
handleRoleFilterQueryClear() {
this.setState({
roleFilterQuery: '',
});
}
}
const GuildSettingsMembersWrapped = Flux.connectStores(
[GuildSettingsStore, UserSettingsStore, GuildMemberStore, StreamerModeStore],
() => {
const {guild, searchQuery, selectedRoleId} = GuildSettingsStore.getProps();
return {
guild,
selectedRoleId,
searchQuery: searchQuery || '',
members: GuildMemberStore.getMembers(guild.id),
theme: UserSettingsStore.theme,
currentUser: UserStore.getCurrentUser(),
canPrune: PermissionStore.can(Permissions.KICK_MEMBERS, guild),
streamerMode: StreamerModeStore.enabled,
};
}
)(GuildSettingsMembers);
export default GuildSettingsMembersWrapped;
// WEBPACK FOOTER //
// ./discord_app/components/guild_settings/GuildSettingsMembers.js