473 lines
14 KiB
JavaScript
Executable file
473 lines
14 KiB
JavaScript
Executable file
import React from 'react';
|
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
|
import Flux from '../../lib/flux';
|
|
import Platforms from '../../lib/Platforms';
|
|
import Alert from '../Alert';
|
|
import GuildIcon from '../common/GuildIcon';
|
|
import {FormSection, FormTitle, FormTitleTags, FormText, FormTextTypes, FormItem, FormDivider} from '../../uikit/form';
|
|
import Card from '../../uikit/Card';
|
|
import Spinner from '../../uikit/Spinner';
|
|
import Button, {ButtonLooks, ButtonColors, ButtonSizes} from '../../uikit/Button';
|
|
import SwitchItem from '../../uikit/SwitchItem';
|
|
import EmptyState, {EmptyStateText, EmptyStateImage} from '../../uikit/EmptyState';
|
|
import ConnectAccountButton, {authorize} from '../common/ConnectAccountButton';
|
|
import StreamerModeEnabled from '../StreamerModeEnabled';
|
|
import ConnectedAccountRecord from '../../records/ConnectedAccountRecord';
|
|
import ConnectedAccountsStore from '../../stores/ConnectedAccountsStore';
|
|
import GuildStore from '../../stores/GuildStore';
|
|
import StreamerModeStore from '../../stores/StreamerModeStore';
|
|
import UserSettingsStore from '../../stores/UserSettingsStore';
|
|
import DetectedPlatformAccountsStore from '../../stores/DetectedPlatformAccountsStore';
|
|
import ModalActionCreators from '../../actions/ModalActionCreators';
|
|
import ConnectedAccountsActionCreators from '../../actions/ConnectedAccountsActionCreators';
|
|
import UserSettingsActionCreators from '../../actions/UserSettingsActionCreators';
|
|
import i18n from '../../i18n';
|
|
import {Colors, UNSAFE_PLATFORM_TYPES, VERIFIABLE_PLATFORM_TYPES} from '../../Constants';
|
|
import NativeUtils from '../../utils/NativeUtils';
|
|
import classNames from 'classnames';
|
|
import '../../styles/user_settings_connections.styl';
|
|
import '../../styles/elevations.styl';
|
|
|
|
const ThemedEmptyState = Flux.connectStores([UserSettingsStore], () => ({
|
|
theme: UserSettingsStore.theme,
|
|
}))(EmptyState);
|
|
|
|
const Integration = React.createClass({
|
|
mixins: [PureRenderMixin, Flux.StoreListenerMixin(GuildStore, ConnectedAccountsStore)],
|
|
|
|
getStateFromStores() {
|
|
const {integration} = this.props;
|
|
return {
|
|
isJoining: ConnectedAccountsStore.isJoining(integration.id),
|
|
isMember: GuildStore.getGuild(integration.guild.id),
|
|
};
|
|
},
|
|
|
|
render() {
|
|
const {isMember, isJoining} = this.state;
|
|
const {integration} = this.props;
|
|
|
|
let joinButton;
|
|
if (!isMember) {
|
|
joinButton = (
|
|
<Button
|
|
size={ButtonSizes.SMALL}
|
|
look={ButtonLooks.GHOST}
|
|
color={ButtonColors.WHITE}
|
|
onClick={this.handleJoinGuild}
|
|
disabled={isJoining}>
|
|
<span>
|
|
{isJoining ? i18n.Messages.JOINING_SERVER : i18n.Messages.JOIN_SERVER}
|
|
</span>
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
let channelName;
|
|
let channelURL;
|
|
switch (integration.type) {
|
|
case 'youtube':
|
|
channelName = integration.account.name;
|
|
channelURL = `gaming.youtube.com/channel/${integration.account.id}`;
|
|
break;
|
|
case 'twitch':
|
|
default:
|
|
channelName = channelURL = `twitch.tv/${integration.account.name}`;
|
|
}
|
|
|
|
return (
|
|
<div className="integration margin-top-8">
|
|
<div>
|
|
<GuildIcon size="small" guild={integration.guild} />
|
|
<div className="integration-inner">
|
|
<FormText>
|
|
{integration.guild.toString()}
|
|
</FormText>
|
|
<a className="channel-link" href={`//${channelURL}`} target="_blank">
|
|
{channelName}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{joinButton}
|
|
</div>
|
|
);
|
|
},
|
|
|
|
// Handlers
|
|
|
|
handleJoinGuild() {
|
|
ConnectedAccountsActionCreators.joinServer(this.props.integration.id);
|
|
},
|
|
});
|
|
|
|
class ConnectedAccount extends React.PureComponent {
|
|
static propTypes = {
|
|
onDisconnect: React.PropTypes.func.isRequired,
|
|
account: React.PropTypes.instanceOf(ConnectedAccountRecord).isRequired,
|
|
};
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
const {friendSync, visibility, verified} = props.account;
|
|
|
|
this.state = {
|
|
friendSync,
|
|
visibility,
|
|
verified,
|
|
};
|
|
}
|
|
|
|
componentWillReceiveProps({account}) {
|
|
const {friendSync, visibility, verified} = account;
|
|
|
|
// The user just verified their connection
|
|
if (!this.state.verified && verified) {
|
|
this.handleInProgressVisibilityChange();
|
|
}
|
|
|
|
this.setState({
|
|
friendSync,
|
|
visibility,
|
|
verified,
|
|
});
|
|
}
|
|
|
|
renderHeader(platform) {
|
|
const {account} = this.props;
|
|
|
|
return (
|
|
<div className="connection-header margin-bottom-20">
|
|
<img className="connection-icon no-user-drag" src={platform.icon.color} />
|
|
<div>
|
|
<FormText className="connection-account-value">
|
|
{account.name}
|
|
</FormText>
|
|
<FormText className="connection-account-label" type={FormTextTypes.DESCRIPTION}>
|
|
{i18n.Messages.ACCOUNT_NAME}
|
|
</FormText>
|
|
</div>
|
|
<div className="connection-delete flex-center" onClick={this.handleDisconnect}>
|
|
<span>{i18n.Messages.SERVICE_CONNECTIONS_DISCONNECT}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderConnectionOptions() {
|
|
const {visibility, friendSync} = this.state;
|
|
const {account} = this.props;
|
|
|
|
let syncFriendsCheckbox;
|
|
if (UNSAFE_PLATFORM_TYPES.has(account.type)) {
|
|
syncFriendsCheckbox = (
|
|
<SwitchItem
|
|
className="connection-option-switch margin-bottom-20"
|
|
clear
|
|
hideBorder
|
|
value={friendSync}
|
|
onChange={this.handleFriendSyncChange}>
|
|
{i18n.Messages.SYNC_FRIENDS}
|
|
</SwitchItem>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="connection-options-wrapper">
|
|
<div className="connection-options">
|
|
<SwitchItem
|
|
className="connection-option-switch margin-bottom-20"
|
|
clear
|
|
hideBorder
|
|
value={visibility === 1}
|
|
onChange={this.handleVisibilityChange}>
|
|
{i18n.Messages.DISPLAY_ON_PROFILE}
|
|
</SwitchItem>
|
|
{syncFriendsCheckbox}
|
|
</div>
|
|
<div />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderIntegrations(platform) {
|
|
const {account} = this.props;
|
|
|
|
// Grab the name in all caps for Colors
|
|
const platformName = platform.name.toUpperCase().replace(/\./g, '');
|
|
|
|
if (account.revoked) {
|
|
return (
|
|
<FormItem className="integrations-wrapper">
|
|
<FormTitle tag={FormTitleTags.H5}>
|
|
<span style={{color: Colors[`${platformName}_200`]}}>{i18n.Messages.SUB_ENABLED_SERVERS}</span>
|
|
</FormTitle>
|
|
|
|
<FormText>
|
|
{i18n.Messages.CONNECTED_ACCOUNT_REVOKED.format({
|
|
onReconnect: this.handleReconnect,
|
|
})}
|
|
</FormText>
|
|
</FormItem>
|
|
);
|
|
} else if (account.integrations.length > 0) {
|
|
return (
|
|
<FormItem className="integrations-wrapper">
|
|
<FormTitle tag={FormTitleTags.H5} className="integration-sub-enabled-title">
|
|
<span style={{color: Colors[`${platformName}_200`]}}>
|
|
{i18n.Messages.SUB_ENABLED_SERVERS}
|
|
</span>
|
|
</FormTitle>
|
|
|
|
<div className="connection-integrations-inner">
|
|
{account.integrations.map(integration =>
|
|
<Integration key={integration.id} integration={integration} color={platform.color} />
|
|
)}
|
|
</div>
|
|
</FormItem>
|
|
);
|
|
}
|
|
}
|
|
|
|
renderDivider() {
|
|
const {revoked, integrations} = this.props.account;
|
|
if (revoked || integrations.length > 0) {
|
|
return <FormDivider className="connected-account-separator" />;
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const {account} = this.props;
|
|
const platform = Platforms.get(account.type);
|
|
|
|
return (
|
|
<div
|
|
className="connection elevation-low margin-bottom-8"
|
|
style={{
|
|
borderColor: platform.color,
|
|
backgroundColor: platform.color,
|
|
}}>
|
|
{this.renderHeader(platform)}
|
|
{this.renderConnectionOptions()}
|
|
{this.renderDivider()}
|
|
{this.renderIntegrations(platform)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Handlers
|
|
|
|
handleReconnect = () => {
|
|
authorize(this.props.account.type);
|
|
};
|
|
|
|
handleDisconnect = () => {
|
|
if (UNSAFE_PLATFORM_TYPES.has(this.props.account.type)) {
|
|
this.props.onDisconnect();
|
|
} else {
|
|
const platform = Platforms.get(this.props.account.type);
|
|
ModalActionCreators.push(props => {
|
|
return (
|
|
<Alert
|
|
title={i18n.Messages.DISCONNECT_ACCOUNT_TITLE.format({
|
|
name: platform.name,
|
|
})}
|
|
body={i18n.Messages.DISCONNECT_ACCOUNT_BODY}
|
|
confirmText={i18n.Messages.DISCONNECT_ACCOUNT}
|
|
cancelText={i18n.Messages.CANCEL}
|
|
onConfirm={this.props.onDisconnect}
|
|
{...props}
|
|
/>
|
|
);
|
|
});
|
|
}
|
|
};
|
|
|
|
handleVisibilityChange = e => {
|
|
const {account} = this.props;
|
|
const visibility = e.currentTarget.checked ? 1 : 0;
|
|
|
|
if (VERIFIABLE_PLATFORM_TYPES.has(account.type) && !this.state.verified) {
|
|
this._inProgressVisibility = visibility;
|
|
authorize(account.type);
|
|
return;
|
|
}
|
|
|
|
this.setState({visibility});
|
|
|
|
ConnectedAccountsActionCreators.setVisibility(account.type, account.id, visibility);
|
|
};
|
|
|
|
handleInProgressVisibilityChange = () => {
|
|
if (this._inProgressVisibility == null) {
|
|
return;
|
|
}
|
|
|
|
const {account} = this.props;
|
|
|
|
ConnectedAccountsActionCreators.setVisibility(account.type, account.id, this._inProgressVisibility);
|
|
|
|
this._inProgressVisibility = null;
|
|
};
|
|
|
|
handleFriendSyncChange = e => {
|
|
const {account} = this.props;
|
|
const friendSync = e.currentTarget.checked;
|
|
this.setState({friendSync});
|
|
|
|
ConnectedAccountsActionCreators.setFriendSync(account.type, account.id, friendSync);
|
|
};
|
|
}
|
|
|
|
const UserSettingsConnections = React.createClass({
|
|
mixins: [
|
|
PureRenderMixin,
|
|
Flux.StoreListenerMixin(
|
|
ConnectedAccountsStore,
|
|
DetectedPlatformAccountsStore,
|
|
UserSettingsStore,
|
|
StreamerModeStore
|
|
),
|
|
],
|
|
|
|
getInitialState() {
|
|
return {
|
|
descriptionWarning: false,
|
|
};
|
|
},
|
|
|
|
getStateFromStores() {
|
|
return {
|
|
hide: StreamerModeStore.hidePersonalInformation,
|
|
fetching: ConnectedAccountsStore.isFetching(),
|
|
accounts: ConnectedAccountsStore.getAccounts(),
|
|
notDetectedPlatformTypes: DetectedPlatformAccountsStore.getNotDetectedPlatformTypes(),
|
|
detectPlatformAccounts: UserSettingsStore.detectPlatformAccounts,
|
|
};
|
|
},
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
const {notDetectedPlatformTypes} = this.state;
|
|
if (prevState.notDetectedPlatformTypes !== notDetectedPlatformTypes) {
|
|
this.setState({
|
|
descriptionWarning: true,
|
|
});
|
|
}
|
|
},
|
|
|
|
componentDidMount() {
|
|
ConnectedAccountsActionCreators.fetch();
|
|
},
|
|
|
|
handleDisconnect({type, id, name}) {
|
|
ConnectedAccountsActionCreators.disconnect(type, id, name);
|
|
},
|
|
|
|
handleDetectPlatformAccounts(e) {
|
|
UserSettingsActionCreators.updateRemoteSettings({
|
|
detectPlatformAccounts: e.currentTarget.checked,
|
|
});
|
|
},
|
|
|
|
renderConnectCard() {
|
|
const {descriptionWarning} = this.state;
|
|
|
|
let description = i18n.Messages.CONNECT_ACCOUNT_DESCRIPTION;
|
|
if (descriptionWarning) {
|
|
description = (
|
|
<div className="warning">
|
|
{i18n.Messages.CONNECT_ACCOUNT_NONE_DETECTED}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card
|
|
className={classNames('connect-account-list', {
|
|
'margin-bottom-20': !NativeUtils.embedded,
|
|
})}>
|
|
<FormTitle faded className="margin-bottom-4">
|
|
{i18n.Messages.CONNECT_ACCOUNT_TITLE}
|
|
</FormTitle>
|
|
<FormText className="user-settings-connections-list-description margin-bottom-8">
|
|
{description}
|
|
</FormText>
|
|
<div className="settings-connected-accounts">
|
|
{Platforms.filter(platform => platform.enabled).map(platform =>
|
|
<ConnectAccountButton key={platform.type} type={platform.type} location="User Settings" />
|
|
)}
|
|
</div>
|
|
</Card>
|
|
);
|
|
},
|
|
|
|
renderDetectPlatformAccounts() {
|
|
const {detectPlatformAccounts} = this.state;
|
|
|
|
if (NativeUtils.embedded) {
|
|
return (
|
|
<SwitchItem
|
|
value={detectPlatformAccounts}
|
|
onChange={this.handleDetectPlatformAccounts}
|
|
className="margin-top-20 margin-bottom-20">
|
|
{i18n.Messages.DETECT_PLATFORM_ACCOUNTS}
|
|
</SwitchItem>
|
|
);
|
|
}
|
|
},
|
|
|
|
renderConnectionList() {
|
|
const {fetching, accounts} = this.state;
|
|
let connectionList;
|
|
|
|
if (fetching) {
|
|
connectionList = <Spinner className="margin-top-20" type={Spinner.Type.SPINNING_CIRCLE} />;
|
|
} else if (accounts.length === 0) {
|
|
connectionList = (
|
|
<ThemedEmptyState className="margin-top-40">
|
|
<EmptyStateImage
|
|
darkSrc={require('../../images/empties/empty_user_settings_connections_dark.svg')}
|
|
lightSrc={require('../../images/empties/empty_user_settings_connections_light.svg')}
|
|
width={230}
|
|
height={220}
|
|
/>
|
|
<EmptyStateText note={i18n.Messages.CONNECTED_ACCOUNTS_NONE}>
|
|
{i18n.Messages.CONNECTED_ACCOUNTS_NONE_TITLE}
|
|
</EmptyStateText>
|
|
</ThemedEmptyState>
|
|
);
|
|
} else {
|
|
connectionList = accounts.map((account, i) =>
|
|
<ConnectedAccount key={i} account={account} onDisconnect={this.handleDisconnect.bind(this, account)} />
|
|
);
|
|
}
|
|
|
|
return (
|
|
<FormItem className="connection-list">
|
|
{connectionList}
|
|
</FormItem>
|
|
);
|
|
},
|
|
|
|
render() {
|
|
if (this.state.hide) {
|
|
return <StreamerModeEnabled />;
|
|
}
|
|
|
|
return (
|
|
<FormSection className="user-settings-connections" tag={FormTitleTags.H2} title={i18n.Messages.CONNECTIONS}>
|
|
{this.renderConnectCard()}
|
|
{this.renderDetectPlatformAccounts()}
|
|
{this.renderConnectionList()}
|
|
</FormSection>
|
|
);
|
|
},
|
|
});
|
|
|
|
export default UserSettingsConnections;
|
|
|
|
|
|
|
|
// WEBPACK FOOTER //
|
|
// ./discord_app/components/user_settings/UserSettingsConnections.js
|