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

256 lines
7.6 KiB
JavaScript
Executable file

import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import classNames from 'classnames';
import TimerMixin from 'react-timer-mixin';
import List from './common/List';
import Animated from '../lib/animated';
import shallowEqual from '../../discord_common/js/lib/shallowEqual';
import UserContextMenu from './contextmenus/UserContextMenu';
import BotTag from './common/BotTag';
import Avatar from './common/Avatar';
import Popout from './common/Popout';
import UserPopout from './UserPopout';
import ContextMenu from './common/ContextMenu';
import ChannelMemberStore from '../stores/views/ChannelMemberStore';
import TypingStore from '../stores/TypingStore';
import GuildSyncStore from '../stores/GuildSyncStore';
import WindowStore from '../stores/WindowStore';
import UserSettingsStore from '../stores/UserSettingsStore';
import Flux from '../lib/flux';
import TutorialIndicator from './common/TutorialIndicator';
import PermissionMixin from '../mixins/PermissionMixin';
import {renderActivity, isStreaming} from '../utils/ActivityUtils';
import {ContextMenuTypes, StatusTypes, ThemeTypes} from '../Constants';
import '../styles/channel_members.styl';
const PLACEHOLDER_MEMBER_ROW_HEIGHT = 40; // In sync with the CSS
const ChannelMembersLoading = React.createClass({
mixins: [TimerMixin, PureRenderMixin],
propTypes: {
memberCount: React.PropTypes.number.isRequired,
isSyncing: React.PropTypes.bool.isRequired,
},
getInitialState() {
const {isSyncing} = this.props;
return {
shouldRender: isSyncing,
opacity: new Animated.Value(isSyncing ? 1 : 0),
};
},
componentWillReceiveProps(nextProps) {
if (nextProps.isSyncing && !this.state.shouldRender) {
this.state.opacity.setValue(1);
this.setState({
shouldRender: true,
});
}
},
componentDidUpdate(prevProps) {
if (this.state.shouldRender && !this.props.isSyncing && prevProps.isSyncing) {
// On next frame, start fadeOut
this.setImmediate(() => {
Animated.timing(this.state.opacity, {
toValue: 0,
duration: 170,
easing: Animated.Easing.easeOut,
}).start(() => {
this.setState({
shouldRender: false,
});
});
});
}
},
render() {
if (!this.state.shouldRender) return null;
const rows = [];
for (let i = 0; i < this.props.memberCount; i++) {
rows.push(<div key={i} className="member" />);
}
return (
<Animated.div className="channel-members-loading" style={{opacity: this.state.opacity}}>
<div className="background" />
<div className="heading" />
{rows}
</Animated.div>
);
},
});
const ChannelMember = React.createClass({
mixins: [Flux.StoreListenerMixin(TypingStore)],
propTypes: {
colorString: React.PropTypes.any, // seems to be a string or number right now :|
darkenColor: React.PropTypes.bool,
user: React.PropTypes.object.isRequired,
channelId: React.PropTypes.string.isRequired,
guildId: React.PropTypes.string.isRequired,
status: React.PropTypes.oneOf(Object.values(StatusTypes)),
},
getStateFromStores() {
return {
typing: TypingStore.isTyping(this.props.channelId, this.props.user.id),
};
},
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps, ['channelId']) || !shallowEqual(this.state, nextState);
},
renderUserContextMenu(e) {
ContextMenu.openContextMenu(e, props =>
<UserContextMenu
{...props}
type={ContextMenuTypes.USER_CHANNEL_MEMBERS}
user={this.props.user}
guildId={this.props.guildId}
channelId={this.props.channelId}
/>
);
},
renderUserPopout(props) {
return (
<UserPopout {...props} user={this.props.user} guildId={this.props.guildId} channelId={this.props.channelId} />
);
},
renderContent() {
const {user, colorString, darkenColor, activity, status, nick} = this.props;
let activityNode;
if (activity) {
activityNode = <div className="member-activity">{renderActivity(activity)}</div>;
}
let color;
if (colorString) {
color = {color: colorString};
if (darkenColor) {
color.WebkitFilter = 'grayscale(50%)';
}
}
return (
<div
onContextMenu={this.renderUserContextMenu}
className={classNames('member', 'member-status', `member-status-${status}`)}>
<Avatar user={user} status={status} streaming={isStreaming(activity)} typing={this.state.typing} />
<div className="member-inner">
<div className="member-username" style={color}>
<span className="member-username-inner">{nick}</span>
{user.bot ? <BotTag /> : null}
</div>
{activityNode}
</div>
</div>
);
},
render() {
return (
<Popout closeOnScroll={false} render={this.renderUserPopout} position={Popout.LEFT} offsetX={15}>
{this.renderContent()}
</Popout>
);
},
});
const ChannelMembers = React.createClass({
mixins: [Flux.StoreListenerMixin(ChannelMemberStore, GuildSyncStore, WindowStore), PermissionMixin],
getStateFromStores() {
return {
...ChannelMemberStore.getMemberGroups(this.props.channel.id),
isSyncing: GuildSyncStore.isSyncing(this.props.channel['guild_id']),
windowSize: WindowStore.windowSize(),
theme: UserSettingsStore.theme,
};
},
shouldComponentUpdate(nextProps, nextState) {
return (
nextState.version !== this.state.version ||
this.didPermissionsUpdate(nextState, nextProps.channel) ||
nextState.isSyncing !== this.state.isSyncing ||
(!nextState.isSyncing && nextState.windowSize.height !== this.state.windowSize.height)
);
},
componentWillReceiveProps(nextProps) {
this.setState({
...ChannelMemberStore.getMemberGroups(nextProps.channel.id),
isSyncing: GuildSyncStore.isSyncing(nextProps.channel['guild_id']),
});
},
renderSection(section) {
const {key, title, users} = this.state.memberGroups[section];
return <h2 key={key}>{title}{users.length}</h2>;
},
renderRow(section, row) {
const {channel} = this.props;
const {darken, users} = this.state.memberGroups[section];
const props = users[row];
return (
<ChannelMember
key={props.user.id}
{...props}
darkenColor={darken}
channelId={channel.id}
guildId={channel['guild_id']}
/>
);
},
render() {
const {theme} = this.state;
const sections = this.state.memberGroups.map(memberGroup => memberGroup.users.length);
const memberCount = Math.ceil(this.state.windowSize.height / PLACEHOLDER_MEMBER_ROW_HEIGHT);
// FIXME: Fix these colors when updating channel members to new theme colors
const backgroundColor = theme === ThemeTypes.LIGHT ? '#f3f3f3' : '#2e3136';
return (
<TutorialIndicator
tutorialId="whos-online"
position={TutorialIndicator.LEFT}
offsetYPercent={-50}
offsetX={16}
offsetY={16}>
<div className="channel-members-wrap">
<List
className="channel-members"
paddingTop={0}
backgroundColor={backgroundColor}
theme={List.Themes.GHOST}
sectionHeight={50}
rowHeight={40}
renderSection={this.renderSection}
renderRow={this.renderRow}
sections={sections}
/>
<ChannelMembersLoading memberCount={memberCount} isSyncing={this.state.isSyncing} />
</div>
</TutorialIndicator>
);
},
});
export default ChannelMembers;
// WEBPACK FOOTER //
// ./discord_app/components/ChannelMembers.js