198 lines
5.9 KiB
JavaScript
Executable file
198 lines
5.9 KiB
JavaScript
Executable file
/* @flow */
|
|
|
|
import React, {PropTypes} from 'react';
|
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
|
import classNames from 'classnames';
|
|
import Flux from '../lib/flux';
|
|
import i18n from '../i18n';
|
|
import lodash from 'lodash';
|
|
import DiscordTag from './common/DiscordTag';
|
|
import Avatar from './common/Avatar';
|
|
import Protip from './common/Protip';
|
|
import Note from './common/Note';
|
|
import QuickMessage from './common/QuickMessage';
|
|
import MemberRolesList from './MemberRolesList';
|
|
import {renderActivity, isStreaming, getStreamURL} from '../utils/ActivityUtils';
|
|
import PresenceStore from '../stores/PresenceStore';
|
|
import UserSettingsStore from '../stores/UserSettingsStore';
|
|
import UserStore from '../stores/UserStore';
|
|
import GuildMemberStore from '../stores/GuildMemberStore';
|
|
import GuildStore from '../stores/GuildStore';
|
|
import UserProfileModalActionCreators from '../actions/UserProfileModalActionCreators';
|
|
import NicknameUtils from '../utils/NicknameUtils';
|
|
import type GuildRecord from '../records/GuildRecord';
|
|
import type {Activity} from '../utils/ActivityUtils';
|
|
import '../styles/user_popout.styl';
|
|
|
|
type GuildMember = ?{
|
|
userId: string,
|
|
nick: ?string,
|
|
roles: Array<string>,
|
|
colorString: ?string,
|
|
hoistRoleId: ?string,
|
|
};
|
|
|
|
const BodySection = ({name, label, body}) =>
|
|
<div className={`section ${name}`}>
|
|
<div className="label">
|
|
<span className={`icon icon-${name}`} />
|
|
{label}
|
|
</div>
|
|
{body}
|
|
</div>;
|
|
|
|
BodySection.propTypes = {
|
|
name: PropTypes.string.isRequired,
|
|
label: PropTypes.string.isRequired,
|
|
body: PropTypes.any,
|
|
};
|
|
|
|
const UserPopout = React.createClass({
|
|
mixins: [Flux.LazyStoreListenerMixin(GuildMemberStore), PureRenderMixin],
|
|
|
|
propTypes: {
|
|
guildId: PropTypes.string,
|
|
channelId: PropTypes.string,
|
|
user: PropTypes.object.isRequired,
|
|
popoutKey: PropTypes.string,
|
|
setNote: PropTypes.bool,
|
|
onClose: PropTypes.func,
|
|
},
|
|
|
|
getDefaultProps() {
|
|
return {
|
|
setNote: false,
|
|
};
|
|
},
|
|
|
|
getInitialState() {
|
|
return this.getStateFromStores();
|
|
},
|
|
|
|
getStateFromStores(): {
|
|
me: boolean,
|
|
guild: ?GuildRecord,
|
|
activity: ?Activity,
|
|
userRoles: Array<string>,
|
|
guildMember: GuildMember,
|
|
status: ?string,
|
|
nickname: string,
|
|
} {
|
|
const {guildId, user, channelId} = this.props;
|
|
const currentUser = UserStore.getCurrentUser();
|
|
const me = currentUser === user;
|
|
const guildMember = guildId != null ? GuildMemberStore.getMember(guildId, user.id) : null;
|
|
const userRoles = guildMember ? guildMember.roles : [];
|
|
const nickname = NicknameUtils.getNickname(guildId, channelId, user);
|
|
return {
|
|
me,
|
|
guild: guildId != null ? GuildStore.getGuild(guildId) : null,
|
|
activity: PresenceStore.getActivity(user.id),
|
|
status: user.isNonUserBot() ? null : PresenceStore.getStatus(user.id),
|
|
userRoles,
|
|
guildMember,
|
|
nickname,
|
|
};
|
|
},
|
|
|
|
close() {
|
|
const {onClose} = this.props;
|
|
onClose && onClose();
|
|
},
|
|
|
|
handleViewProfile() {
|
|
UserProfileModalActionCreators.open(this.props.user.id);
|
|
this.close();
|
|
},
|
|
|
|
render() {
|
|
const {me, guild, activity, status, userRoles, guildMember, nickname} = this.state;
|
|
// eslint-disable-next-line no-unused-vars
|
|
const {user, popoutKey, setNote, onClose, guildId, channelId, ...props} = this.props;
|
|
const isNonUserBot = user.isNonUserBot();
|
|
const numRoles = userRoles.length;
|
|
|
|
// Check against all mutual Guilds to see if your DM permissions allow you to send a message
|
|
let canDM = false;
|
|
if (!me) {
|
|
lodash(GuildMemberStore.memberOf(user.id))
|
|
.map(guildId => UserSettingsStore.isGuildRestricted(guildId))
|
|
.forEach(isRestricted => {
|
|
if (!isRestricted) {
|
|
canDM = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
let roles = null;
|
|
if (!!guildMember) {
|
|
roles = (
|
|
<BodySection
|
|
name="roles"
|
|
label={i18n.Messages.ROLES_LIST.format({numRoles})}
|
|
body={<MemberRolesList guild={guild} user={user} userRoles={userRoles} popoutKey={popoutKey} />}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const streaming = isStreaming(activity);
|
|
let liveOnTwitch;
|
|
if (streaming) {
|
|
liveOnTwitch = (
|
|
<a className="live-on-twitch" href={getStreamURL(activity)} target="_blank" rel="noreferrer">
|
|
<span className="live" />
|
|
{i18n.Messages.LIVE_ON_TWITCH}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="user-popout" {...props}>
|
|
<header className={classNames('header', {streaming})}>
|
|
|
|
<div
|
|
className={classNames('avatar-wrapper', {'no-hover': isNonUserBot})}
|
|
onClick={!isNonUserBot && this.handleViewProfile}>
|
|
<div className="avatar-hint">View Profile</div>
|
|
<Avatar user={user} size="popout" status={status} animate />
|
|
</div>
|
|
|
|
<div className={classNames('username-wrapper', {'has-nickname': nickname && nickname.length})}>
|
|
{nickname && <div className="nickname">{nickname}</div>}
|
|
|
|
<DiscordTag user={user} invertBotColor={true} hideDiscriminator={isNonUserBot} />
|
|
|
|
{activity && <div className="activity">{renderActivity(activity)}</div>}
|
|
|
|
{liveOnTwitch}
|
|
</div>
|
|
</header>
|
|
|
|
{!isNonUserBot &&
|
|
<div className="body">
|
|
{roles}
|
|
<BodySection name="notes" label={i18n.Messages.NOTE} body={<Note userId={user.id} autoFocus={setNote} />} />
|
|
</div>}
|
|
|
|
{!isNonUserBot &&
|
|
<div className="footer">
|
|
{canDM &&
|
|
<div className="quick-message-wrapper">
|
|
<QuickMessage user={user} autoFocus={!setNote} onClose={onClose} />
|
|
</div>}
|
|
|
|
<div className="update-notice">
|
|
<Protip type="inline" tip={i18n.Messages.CONTEXT_MENU_HINT} />
|
|
</div>
|
|
</div>}
|
|
</div>
|
|
);
|
|
},
|
|
});
|
|
|
|
export default UserPopout;
|
|
|
|
|
|
|
|
// WEBPACK FOOTER //
|
|
// ./discord_app/components/UserPopout.js
|