2017-06-08_509bba0/509bba0_unpacked_with_node_.../discord_app/components/InstantInviteModal.js

371 lines
11 KiB
JavaScript
Raw Normal View History

2022-07-26 17:06:20 +00:00
/* @flow */
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Animated from '../lib/animated';
import Select from 'react-select';
import i18n from '../i18n';
import InstantInvite from './InstantInvite';
import Flux from '../lib/flux';
import AnalyticsUtils from '../utils/AnalyticsUtils';
import InstantInviteActionCreators from '../actions/InstantInviteActionCreators';
import InstantInviteStore from '../stores/InstantInviteStore';
import StreamerModeStore from '../stores/StreamerModeStore';
import InstantInviteUtils from '../utils/InstantInviteUtils';
import GuildChannelStore from '../stores/views/GuildChannelStore';
import TransitionGroup from '../lib/transitions/TransitionGroup';
import Checkbox from './common/Checkbox';
import InvitePeriodExperimentStore from '../stores/experiments/InvitePeriodExperimentStore';
import createExperimentContainer from './experiments/ExperimentContainer';
import Spinner from './common/Spinner';
import Tooltip from './common/Tooltip';
import {ChannelTypes, ExperimentTypes, ExperimentBuckets} from '../Constants';
import '../styles/instant_invite_modal.styl';
const LOCATION = 'Invite Modal';
const MAX_AGE_OPTIONS = InstantInviteUtils.getMaxAgeOptions;
const MAX_USES_OPTIONS = InstantInviteUtils.getMaxUsesOptions;
function createAnimationMixin(reverse = false, duration = 250, easing = Animated.Easing.inOut(Animated.Easing.cubic)) {
return {
getInitialState() {
return {
opacity: new Animated.Value(0),
};
},
componentDidAppear() {
this.state.opacity.setValue(1);
},
componentWillEnter(callback) {
Animated.timing(this.state.opacity, {toValue: 1, duration, easing}).start(callback);
},
componentWillLeave(callback) {
Animated.timing(this.state.opacity, {toValue: 0, duration, easing}).start(callback);
},
getAnimatedStyle() {
return {
opacity: this.state.opacity,
};
},
};
}
const BasicInstantInviteModal = React.createClass({
mixins: [createAnimationMixin()],
render() {
const {invite, guild, hide, maxAge, handleExpiresChange, showAdvanced} = this.props;
let doesNotExpire = false;
let instantInvite;
let expireText;
if (!invite) {
instantInvite = <Spinner />;
} else {
instantInvite = (
<InstantInvite code={invite.code} copyText={i18n.Messages.COPY} hidden={hide} location={LOCATION} />
);
doesNotExpire = maxAge == MAX_AGE_OPTIONS[0].value;
if (doesNotExpire) {
expireText = i18n.Messages.INVITE_LINKS_NEVER_EXPIRES;
} else if (maxAge == MAX_AGE_OPTIONS[1].value) {
expireText = i18n.Messages.INVITE_LINKS_EXPIRE_AFTER_BY_DEFAULT;
} else {
expireText = i18n.Messages.INVITE_LINKS_EXPIRE_AFTER_1_DAY;
}
}
return (
<Animated.form id="instant-invite-modal" className="form" style={this.getAnimatedStyle()}>
<div className="form-header">
<header>
{i18n.Messages.INVITE_TO_SERVER_NAME.format({guildName: guild.name})}
</header>
<div className="blurb">
{i18n.Messages.SHARE_INVITE_LINK_FOR_ACCESS}
</div>
</div>
<div className="form-inner">
{instantInvite}
<div className="expire-text">{expireText}</div>
</div>
<div className="form-actions">
<Checkbox checked={doesNotExpire} onChange={handleExpiresChange}>
{i18n.Messages.SET_INVITE_LINK_NEVER_EXPIRE}
</Checkbox>
<Tooltip text={i18n.Messages.LINK_SETTINGS}>
<button className="advanced" onClick={showAdvanced} />
</Tooltip>
</div>
</Animated.form>
);
},
});
const AdvancedInstantInviteModal = React.createClass({
mixins: [createAnimationMixin(true)],
render() {
const {
maxAge,
maxUses,
temporary,
handleTemporaryChange,
handleGenerateNewLink,
hideAdvanced,
handleMaxUsesChange,
handleExpireAfterChange,
} = this.props;
const maxAgeValue = MAX_AGE_OPTIONS.find(age => age.value == maxAge);
const maxUsesValue = MAX_USES_OPTIONS.find(uses => uses.value == maxUses);
return (
<Animated.form id="instant-invite-modal-advanced" className="form" style={this.getAnimatedStyle()}>
<div className="form-inner">
<div className="control-groups">
<div className="control-group">
<label>{i18n.Messages.EXPIRE_AFTER}</label>
<Select
value={maxAgeValue}
clearable={false}
searchable={false}
options={MAX_AGE_OPTIONS}
onChange={handleExpireAfterChange}
/>
</div>
<div className="control-group">
<label>{i18n.Messages.MAX_NUMBER_OF_USES}</label>
<Select
value={maxUsesValue}
clearable={false}
searchable={false}
options={MAX_USES_OPTIONS}
onChange={handleMaxUsesChange}
/>
</div>
</div>
<div className="control-group">
<Checkbox checked={temporary} onChange={handleTemporaryChange}>
{i18n.Messages.GRANT_TEMPORARY_MEMBERSHIP}
</Checkbox>
<div className="help-text">
{i18n.Messages.TEMPORARY_MEMBERSHIP_EXPLANATION}
</div>
</div>
</div>
<div className="form-actions">
<button type="button" className="btn btn-default" onClick={hideAdvanced}>
{i18n.Messages.CANCEL}
</button>
<button type="submit" className="btn btn-primary" onClick={handleGenerateNewLink}>
{i18n.Messages.GENERATE_A_NEW_LINK}
</button>
</div>
</Animated.form>
);
},
});
const InstantInviteModal = React.createClass({
mixins: [Flux.LazyStoreListenerMixin(InstantInviteStore, StreamerModeStore), PureRenderMixin],
getInitialState() {
return {
showAdvanced: false,
maxUses: MAX_USES_OPTIONS[0].value,
temporary: false,
channel: this.getChannel(),
...this.getStateFromStores(),
};
},
getStateFromStores() {
const invite = InstantInviteStore.getInvite(this.getChannel().id);
const maxAge = invite ? invite.maxAge : MAX_AGE_OPTIONS[1].value;
const savedMaxAge = maxAge == MAX_USES_OPTIONS[0].value ? MAX_AGE_OPTIONS[1].value : MAX_AGE_OPTIONS[0].value;
return {
invite,
maxAge,
savedMaxAge,
hide: StreamerModeStore.hideInstantInvites,
};
},
componentDidMount() {
const validate = this.state.invite ? this.state.invite.code : null;
const experimentBucket = this.props.experiment ? this.props.experiment.bucket : 0;
const maxAge = (experimentBucket => {
switch (experimentBucket) {
case ExperimentBuckets.TREATMENT_1:
case ExperimentBuckets.TREATMENT_2:
return MAX_AGE_OPTIONS[5].value; // 1 Day
default:
return MAX_AGE_OPTIONS[1].value;
}
})(experimentBucket);
InstantInviteActionCreators.createInvite(
this.state.channel.id,
{
validate,
max_age: maxAge, // eslint-disable-line camelcase
},
'InstantInviteModal Mount'
);
let {source} = this.props;
if (source != null) {
if (window.inviteButtonSource) {
source = window.inviteButtonSource;
window.inviteButtonSource = undefined;
}
AnalyticsUtils.track('open_popout', {
Type: 'Instant Invite Modal',
Source: source,
});
}
},
getChannel() {
const {channel, guild} = this.props;
if (channel) {
return channel;
}
if (this.state && this.state.channel) {
return this.state.channel;
}
const channels = GuildChannelStore.getChannels(guild.id)[ChannelTypes.GUILD_TEXT];
return channels.find(channel => channel.channel.id == guild.id).channel;
},
handleExpiresChange() {
this.createInvite(this.state.savedMaxAge, this.state.maxUses, this.state.temporary, 'InstantInviteModal');
this.setState({
maxAge: this.state.savedMaxAge,
savedMaxAge: this.state.maxAge,
});
},
handleExpireAfterChange(value: string) {
this.setState({
maxAge: value,
savedMaxAge: value == MAX_AGE_OPTIONS[0].value ? MAX_AGE_OPTIONS[1].value : MAX_AGE_OPTIONS[0].value,
});
},
handleMaxUsesChange(value: string) {
this.setState({
maxUses: value,
});
},
handleTemporaryChange(e: Event) {
this.setState({
// $FlowFixMe
temporary: e.currentTarget.checked,
});
},
showAdvanced(e: Event) {
e.preventDefault();
this.setState({
showAdvanced: true,
});
},
hideAdvanced(e: Event) {
e.preventDefault();
this.setState({
showAdvanced: false,
});
},
handleGenerateNewLink(e: Event) {
e.preventDefault();
this.createInvite(this.state.maxAge, this.state.maxUses, this.state.temporary, 'InstantInviteModal Regenerate');
this.setState({showAdvanced: false});
},
createInvite(maxAge: string | number, maxUses: string | number, temporary: boolean) {
InstantInviteActionCreators.createInvite(
this.state.channel.id,
{
/* eslint-disable camelcase */
max_age: parseInt(maxAge, 10),
max_uses: parseInt(maxUses, 10),
/* eslint-enable camelcase */
temporary: temporary,
},
'InstantInviteModal'
);
},
render() {
const {showAdvanced, maxAge, maxUses, temporary, hide, invite} = this.state;
const {guild} = this.props;
const content = showAdvanced
? <AdvancedInstantInviteModal
maxAge={maxAge}
maxUses={maxUses}
temporary={temporary}
handleTemporaryChange={this.handleTemporaryChange}
handleGenerateNewLink={this.handleGenerateNewLink}
handleMaxUsesChange={this.handleMaxUsesChange}
handleExpireAfterChange={this.handleExpireAfterChange}
hideAdvanced={this.hideAdvanced}
key="instant-invite-modal"
/>
: <BasicInstantInviteModal
invite={invite}
guild={guild}
hide={hide}
maxAge={maxAge}
handleExpiresChange={this.handleExpiresChange}
showAdvanced={this.showAdvanced}
key="instant-invite-modal-advanced"
/>;
return (
<TransitionGroup className="instant-invite-modal theme-light" component="div">
{content}
</TransitionGroup>
);
},
});
const renderComponent = (props, experimentDescriptor) => {
return <InstantInviteModal {...props} experiment={experimentDescriptor} />;
};
export default createExperimentContainer(InvitePeriodExperimentStore, {
[ExperimentTypes.NONE]: props => <InstantInviteModal {...props} />,
[ExperimentTypes.USER]: {
[ExperimentBuckets.CONTROL]: renderComponent,
[ExperimentBuckets.TREATMENT_1]: renderComponent,
[ExperimentBuckets.TREATMENT_2]: renderComponent,
},
});
// WEBPACK FOOTER //
// ./discord_app/components/InstantInviteModal.js