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

585 lines
16 KiB
JavaScript
Executable file

/* @flow */
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Flux from '../lib/flux';
import i18n from '../i18n';
import Platforms from '../lib/Platforms';
import Avatar from './common/Avatar';
import TabBar, {TabBarItem, TabBarTypes} from './common/TabBar';
import GuildIcon from './common/GuildIcon';
import UserProfileModalStore from '../stores/UserProfileModalStore';
import UserStore from '../stores/UserStore';
import StreamerModeStore from '../stores/StreamerModeStore';
import {popLayer} from '../actions/LayerActionCreators';
import UserProfileModalActionCreators from '../actions/UserProfileModalActionCreators';
import RelationshipActionCreators from '../actions/RelationshipActionCreators';
import ChannelActionCreators from '../actions/ChannelActionCreators';
import UserSettingsModalActionCreators from '../actions/UserSettingsModalActionCreators';
import ContextMenu from './common/ContextMenu';
import UserContextMenu from './contextmenus/UserContextMenu';
import GuildContextMenu from './contextmenus/GuildContextMenu';
import Scroller from './common/Scroller';
import Spinner from './common/Spinner';
import Popout from './common/Popout';
import Note from './common/Note';
import Tooltip from './common/Tooltip';
import DiscordTag from './common/DiscordTag';
import RouterUtils from '../utils/RouterUtils';
import {renderActivity} from '../utils/ActivityUtils';
import UserRecord from '../records/UserRecord';
import type GuildRecord from '../records/GuildRecord';
import '../styles/user_profile_modal.styl';
import '../styles/common/context_menu.styl';
import {
RelationshipTypes,
UserProfileSections,
Routes,
ContextMenuTypes,
UserSettingsSections,
UserFlags,
MARKETING_URLS,
} from '../Constants';
// User Info
const UserInfo = React.createClass({
mixins: [PureRenderMixin, Flux.StoreListenerMixin(StreamerModeStore)],
propTypes: {
user: React.PropTypes.instanceOf(UserRecord).isRequired,
connectedAccounts: React.PropTypes.array.isRequired,
},
getStateFromStores() {
return {
hide: StreamerModeStore.hidePersonalInformation,
};
},
render() {
if (this.state.hide) {
return (
<div className="empty">
<div className="empty-icon-streamer-mode" />
<div className="empty-text">{i18n.Messages.STREAMER_MODE_ENABLED}</div>
</div>
);
}
let connectedAccounts;
if (this.props.connectedAccounts != null && this.props.connectedAccounts.length > 0) {
const accountRows = this.props.connectedAccounts.map((account, i) => {
const platform = Platforms.get(account.type);
const platformUrl = platform.getPlatformUserUrl && platform.getPlatformUserUrl(account);
let openIcon;
if (platformUrl) {
openIcon = (
<a href={platformUrl} rel="noreferrer" target="_blank">
<div className="connected-account-open-icon" />
</a>
);
}
let verifiedIcon;
if (account.verified) {
verifiedIcon = (
<Tooltip text={i18n.Messages.CONNECTION_VERIFIED}>
<i className="connected-account-verified-icon" />
</Tooltip>
);
}
return (
<div key={i} className="connected-account">
<img className="connected-account-icon" src={platform.icon.color} />
<div className="connected-account-name-inner">
<div className="connected-account-name">{account.name}</div>
{verifiedIcon}
</div>
{openIcon}
</div>
);
});
connectedAccounts = (
<div className="section">
<div className="connected-accounts">
{accountRows}
</div>
</div>
);
}
return (
<Scroller className="guilds" fade={true}>
<div className="section">
<div className="section-header">{i18n.Messages.NOTE}</div>
<Note userId={this.props.user.id} autoFocus={false} />
</div>
{connectedAccounts}
</Scroller>
);
},
});
// Mutual Guilds
const GuildRow = ({
guild,
nick,
onSelect,
onContextMenu,
}: {
guild: GuildRecord,
nick: string,
onSelect: Function,
onContextMenu: Function,
}) =>
<div className="guild" onClick={() => onSelect(guild.id)} onContextMenu={event => onContextMenu(event, guild)}>
<GuildIcon guild={guild} size="large" />
<div className="guild-inner">
<div className="guild-name">{guild.toString()}</div>
<div className="guild-nick">{nick}</div>
</div>
</div>;
const MutualGuilds = React.createClass({
propTypes: {
mutualGuilds: React.PropTypes.array.isRequired,
},
handleSelect(guildId) {
RouterUtils.transitionTo(Routes.GUILD(guildId));
UserProfileModalActionCreators.close();
popLayer();
},
handleContextMenu(event, guild) {
ContextMenu.openContextMenu(event, props =>
<GuildContextMenu {...props} type={ContextMenuTypes.GUILD_PROFILE_MUTUAL} guild={guild} />
);
},
render() {
if (this.props.mutualGuilds.length === 0) {
return (
<div className="empty">
<div className="empty-icon-guilds" />
<div className="empty-text">{i18n.Messages.NO_MUTUAL_GUILDS}</div>
</div>
);
}
const guildRows = this.props.mutualGuilds.map(({guild, nick}) =>
<GuildRow
key={guild.id}
guild={guild}
nick={nick}
onSelect={this.handleSelect}
onContextMenu={this.handleContextMenu}
/>
);
return (
<Scroller className="guilds" fade={true}>
{guildRows}
</Scroller>
);
},
});
// Mutual Friends
const FriendRow = ({
user,
status,
onSelect,
onContextMenu,
}: {
user: UserRecord,
status: string,
onSelect: Function,
onContextMenu: Function,
}) =>
<div className="guild" onClick={() => onSelect(user.id)} onContextMenu={event => onContextMenu(event, user)}>
<Avatar user={user} size="large" status={status} />
<div className="guild-inner">
<DiscordTag user={user} className="guild-name" />
</div>
</div>;
const MutualFriends = React.createClass({
propTypes: {
mutualFriends: React.PropTypes.array,
},
handleSelect(userId) {
UserProfileModalActionCreators.open(userId);
},
handleContextMenu(event, user) {
ContextMenu.openContextMenu(event, props =>
<UserContextMenu {...props} type={ContextMenuTypes.USER_PROFILE_MUTUAL} user={user} />
);
},
render() {
const {mutualFriends} = this.props;
const friendRows = [];
if (!mutualFriends) {
return <div className="empty"><Spinner /></div>;
} else if (mutualFriends.length === 0) {
return (
<div className="empty">
<div className="empty-icon-friends" />
<div className="empty-text">{i18n.Messages.NO_MUTUAL_FRIENDS}</div>
</div>
);
} else {
mutualFriends.forEach(rel => {
friendRows.push(
<FriendRow
key={rel.key}
user={rel.user}
status={rel.status}
onSelect={this.handleSelect}
onContextMenu={this.handleContextMenu}
/>
);
});
}
return (
<Scroller className="guilds" fade={true}>
{friendRows}
</Scroller>
);
},
});
// Footer
const RequestActions = ({user, relationshipType, friendReqSent, handleAddFriend, handleIgnoreFriend}) => {
let actions = null;
if (
user.id !== UserStore.getCurrentUser().id &&
!user.bot &&
relationshipType != RelationshipTypes.FRIEND &&
relationshipType != RelationshipTypes.BLOCKED
) {
if (friendReqSent) {
actions = (
<div className="actions">
<button disabled={true} className="btn disabled">
{i18n.Messages.ADD_FRIEND_BUTTON_AFTER}
</button>
</div>
);
} else if (relationshipType === RelationshipTypes.PENDING_INCOMING) {
actions = (
<div className="actions">
<div className="pending">
<div className="pending-actions">
<button className="btn add-friend" onClick={handleAddFriend}>
{i18n.Messages.FRIEND_REQUEST_ACCEPT}
</button>
<button className="btn reject-friend " onClick={handleIgnoreFriend}>
{i18n.Messages.FRIEND_REQUEST_IGNORE}
</button>
</div>
</div>
</div>
);
} else {
actions = (
<div className="actions">
<button className="btn add-friend" onClick={handleAddFriend}>
{i18n.Messages.ADD_FRIEND_BUTTON}
</button>
</div>
);
}
}
return actions;
};
// Popout additional actions
const ProfileActionsPopout = React.createClass({
propTypes: {
relationshipType: React.PropTypes.number,
onRemoveFriend: React.PropTypes.func.isRequired,
onBlock: React.PropTypes.func.isRequired,
onMessage: React.PropTypes.func.isRequired,
onClose: React.PropTypes.func.isRequired,
},
performAction(action) {
action();
this.props.onClose();
},
render() {
const {relationshipType, onRemoveFriend, onBlock, onMessage} = this.props;
const options = [];
switch (relationshipType) {
case RelationshipTypes.BLOCKED:
options.push(
<div className="item" onClick={this.performAction.bind(this, onRemoveFriend)} key="user-unblock">
{i18n.Messages.UNBLOCK}
</div>
);
break;
case RelationshipTypes.FRIEND:
options.push(
<div className="item danger" onClick={this.performAction.bind(this, onRemoveFriend)} key="user-remove">
{i18n.Messages.REMOVE_FRIEND}
</div>
);
options.push(
<div className="item danger" onClick={this.performAction.bind(this, onBlock)} key="user-block">
{i18n.Messages.BLOCK}
</div>
);
break;
case RelationshipTypes.NONE:
case RelationshipTypes.PENDING_INCOMING:
default:
options.push(
<div className="item danger" onClick={this.performAction.bind(this, onBlock)} key="user-block">
{i18n.Messages.BLOCK}
</div>
);
}
if (relationshipType !== RelationshipTypes.BLOCKED) {
options.push(
<div className="item" onClick={this.performAction.bind(this, onMessage)} key="user-message">
{i18n.Messages.USER_POPOUT_MESSAGE}
</div>
);
}
return (
<div className="context-menu">
{options}
</div>
);
},
});
// MODAL
const UserProfileModal = React.createClass({
mixins: [PureRenderMixin],
statics: {
modalConfig: {
store: UserProfileModalStore,
},
},
propTypes: {
section: React.PropTypes.string.isRequired,
user: React.PropTypes.instanceOf(UserRecord).isRequired,
status: React.PropTypes.string,
activity: React.PropTypes.object,
relationshipType: React.PropTypes.number,
mutualGuilds: React.PropTypes.array,
mutualFriends: React.PropTypes.array,
connectedAccounts: React.PropTypes.array,
premiumSince: React.PropTypes.instanceOf(Date),
flags: React.PropTypes.number,
},
getInitialState() {
return {
friendReqSent: false,
};
},
hasFlag(flag) {
const {user} = this.props;
return (user.flags & flag) === flag;
},
render() {
const {user, status, activity, relationshipType, premiumSince, section: selectedSection} = this.props;
const {friendReqSent} = this.state;
let component;
const props = {};
switch (selectedSection) {
case UserProfileSections.MUTUAL_GUILDS:
component = MutualGuilds;
props.mutualGuilds = this.props.mutualGuilds;
break;
case UserProfileSections.MUTUAL_FRIENDS:
component = MutualFriends;
props.mutualFriends = this.props.mutualFriends;
break;
case UserProfileSections.USER_INFO:
default:
component = UserInfo;
props.user = this.props.user;
props.connectedAccounts = this.props.connectedAccounts;
break;
}
let badgeProps;
if (this.hasFlag(UserFlags.STAFF)) {
badgeProps = {
tooltip: i18n.Messages.STAFF_BADGE_TOOLTIP,
onClick: () => window.open(MARKETING_URLS.COMPANY, '_blank'),
class: 'badge-staff',
};
} else if (this.hasFlag(UserFlags.PARTNER)) {
badgeProps = {
tooltip: i18n.Messages.PARTNER_BADGE_TOOLTIP,
onClick: () => window.open(MARKETING_URLS.PARTNERS, '_blank'),
class: 'badge-partner',
};
} else if (this.hasFlag(UserFlags.HYPESQUAD)) {
badgeProps = {
tooltip: i18n.Messages.HYPESQUAD_BADGE_TOOLTIP,
onClick: () => window.open(MARKETING_URLS.HYPESQUAD, '_blank'),
class: 'badge-hypesquad',
};
} else if (premiumSince != null) {
badgeProps = {
tooltip: i18n.Messages.PREMIUM_BADGE_TOOLTIP.format({date: premiumSince}),
onClick: this.openPremiumSettings,
class: 'badge-premium',
};
}
let badge;
if (badgeProps) {
badge = (
<Tooltip position="top" text={badgeProps.tooltip}>
<div onClick={badgeProps.onClick} className={`profile-badge ${badgeProps.class}`} />
</Tooltip>
);
}
let moreActions;
if (user.id !== UserStore.getCurrentUser().id && !user.isNonUserBot()) {
moreActions = (
<Popout
uniqueId="profile-actions"
closeOnScroll={false}
position={Popout.BOTTOM}
zIndexBoost={1}
animationType="none"
offsetX={-65}
offsetY={-10}
render={props =>
<ProfileActionsPopout
{...props}
relationshipType={relationshipType}
onRemoveFriend={this.handleRemoveFriend}
onBlock={this.handleBlock}
onMessage={this.handleSendMessage}
/>}>
<div className="additional-actions-icon" />
</Popout>
);
}
return (
<div id="user-profile-modal">
<header className="header">
<div className="header-info">
{badge}
<div className="header-info-inner">
<DiscordTag user={user} />
{activity && <div className="activity">{renderActivity(activity)}</div>}
</div>
</div>
<RequestActions
user={user}
relationshipType={relationshipType}
friendReqSent={friendReqSent}
handleAddFriend={this.handleAddFriend}
handleIgnoreFriend={this.handleRemoveFriend}
/>
{moreActions}
</header>
<div className="tab-bar-container">
<TabBar selectedItem={selectedSection} type={TabBarTypes.TOP} onItemSelect={this.handleSectionSelect}>
<TabBarItem key={UserProfileSections.USER_INFO}>
{i18n.Messages.USER_INFO}
</TabBarItem>
<TabBarItem key={UserProfileSections.MUTUAL_GUILDS}>
{i18n.Messages.MUTUAL_GUILDS}
</TabBarItem>
<TabBarItem key={UserProfileSections.MUTUAL_FRIENDS}>
{i18n.Messages.MUTUAL_FRIENDS}
</TabBarItem>
</TabBar>
</div>
<div className="avatar-wrapper">
<Avatar user={user} size="profile" status={status} animate />
</div>
{React.createElement(component, props)}
</div>
);
},
// utils
close() {
UserProfileModalActionCreators.close();
},
// action handlers
handleSendMessage() {
ChannelActionCreators.openPrivateChannel(UserStore.getCurrentUser().id, this.props.user.id);
this.close();
popLayer();
},
handleBlock() {
const {user} = this.props;
RelationshipActionCreators.addRelationship(user.id, {location: 'User Profile'}, RelationshipTypes.BLOCKED);
},
handleAddFriend() {
RelationshipActionCreators.addRelationship(this.props.user.id, {location: 'User Profile'});
this.setState({
friendReqSent: true,
});
},
handleRemoveFriend() {
RelationshipActionCreators.removeRelationship(this.props.user.id, {location: 'User Profile'});
this.setState({
friendReqSent: false,
});
},
handleSectionSelect(selectedSection: string) {
UserProfileModalActionCreators.setSection(selectedSection);
},
openPremiumSettings() {
UserSettingsModalActionCreators.open(UserSettingsSections.PREMIUM);
this.close();
},
});
export default UserProfileModal;
// WEBPACK FOOTER //
// ./discord_app/components/UserProfileModal.js