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

279 lines
8.3 KiB
JavaScript
Executable file

/* @flow */
import React from 'react';
import Flux from '../lib/flux';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import lodash from 'lodash';
import i18n from '../i18n';
import TransitionGroup from '../lib/transitions/TransitionGroup';
import Animated from '../lib/animated';
import {createSound} from '../utils/SoundUtils';
import type {Sound} from '../utils/SoundUtils';
import Flex from '../uikit/Flex';
import Button from '../uikit/Button';
import CallAvatar from '../uikit/CallAvatar';
import createExperimentContainer from './experiments/ExperimentContainer';
import Draggable from './common/Draggable';
import UserSettingsStore from '../stores/UserSettingsStore';
import WindowStore from '../stores/WindowStore';
import StreamerModeStore from '../stores/StreamerModeStore';
import IncomingCallStore from '../stores/IncomingCallStore';
import NotificationStore from '../stores/NotificationStore';
import PrivateChannelCallStore from '../stores/views/PrivateChannelCallStore';
import VideoCallExperimentStore from '../stores/experiments/VideoCallExperimentStore';
import AudioActionCreators from '../actions/AudioActionCreators';
import IncomingCallActionCreators from '../actions/IncomingCallActionCreators';
import CallActionCreators from '../actions/CallActionCreators';
import SelectedChannelActionCreators from '../actions/SelectedChannelActionCreators';
import ChannelRecord from '../records/ChannelRecord';
import ComponentDispatchMixin from '../mixins/ComponentDispatchMixin';
import {ComponentActions, ExperimentBuckets, ExperimentTypes, CallModes} from '../Constants';
import '../styles/incoming_calls.styl';
const IncomingCallModal = React.createClass({
mixins: [Flux.LazyStoreListenerMixin(PrivateChannelCallStore), PureRenderMixin, ComponentDispatchMixin],
getInitialState() {
return {
animate: new Animated.Value(0),
...this.getStateFromStores(),
};
},
getStateFromStores() {
return {
mode: PrivateChannelCallStore.getCallMode(this.props.channel.id),
};
},
getSubscriptions() {
return {
[ComponentActions.CALL_ACCEPT]: this.handleAcceptWithAudio,
[ComponentActions.CALL_DECLINE]: this.handleDeclineCall,
};
},
componentWillEnter(callback) {
Animated.timing(this.state.animate, {
toValue: 1,
duration: 170,
easing: Animated.Easing.inOut(Animated.Easing.back()),
}).start(callback);
},
componentDidAppear() {
this.state.animate.setValue(1);
},
componentWillLeave(callback) {
Animated.timing(this.state.animate, {
toValue: 0,
duration: 170,
easing: Animated.Easing.quad,
}).start(callback);
},
getStyle() {
const {animate} = this.state;
if (this.props.shouldAnimate) {
return Animated.accelerate({
transform: [
{
scale: animate.interpolate({
inputRange: [0, 1],
outputRange: ['0.7', '1'],
}),
},
],
opacity: animate,
});
}
return null;
},
renderAllActions() {
return [
<Button
key="video"
className="action-button margin-top-4"
onClick={this.handleAcceptWithVideo}
color={Button.Colors.GREEN}>
<div className="flex-horizontal flex-align-center">
<img className="action-icon" src={require('../images/calls/ic_videocam_white_16px.svg')} />
{i18n.Messages.VIDEO}
</div>
</Button>,
<Button
key="audio"
className="action-button margin-top-4"
onClick={this.handleAcceptWithAudio}
color={Button.Colors.GREEN}>
<div className="flex-horizontal flex-align-center">
<img className="action-icon" src={require('../images/calls/ic_mic_white_16px.svg')} />
{i18n.Messages.VOICE}
</div>
</Button>,
];
},
renderAudioOnlyActions() {
return (
<Button className="action-button margin-top-8" onClick={this.handleAcceptWithAudio} color={Button.Colors.GREEN}>
<div className="flex-horizontal flex-align-center">
<img className="action-icon" src={require('../images/calls/ic_mic_white_16px.svg')} />
{i18n.Messages.JOIN_CALL}
</div>
</Button>
);
},
renderActions() {
return (
<div className="action-buttons margin-top-20">
{this.props.video && this.state.mode === CallModes.VIDEO
? this.renderAllActions()
: this.renderAudioOnlyActions()}
<Button
className="action-button action-button-decline margin-top-8"
onClick={this.handleDeclineCall}
color={Button.Colors.TRANSPARENT}>
<div className="flex-horizontal flex-align-center">
{i18n.Messages.DECLINE}
</div>
</Button>
</div>
);
},
render() {
const {x, y, channel, shouldAnimate} = this.props;
const avatarURL = channel.getIconURL();
return (
<Draggable
className="incoming-call elevation-high"
selector=".incoming-call-inner"
initialX={x}
initialY={y}
maxX={window.innerWidth}
maxY={window.innerHeight}
onDragEnd={this.handleStop}
disableFlip
dragAnywhere>
<Animated.div className="incoming-call-inner" style={this.getStyle()}>
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.CENTER}>
<CallAvatar className="margin-top-20" ringing={shouldAnimate} src={avatarURL || ''} id={channel.id} />
<div className="members margin-top-20">{channel.toString()}</div>
<div className="subtitle margin-top-8">
{this.state.mode === CallModes.VOICE
? i18n.Messages.INCOMING_CALL_ELLIPSIS
: i18n.Messages.INCOMING_VIDEO_CALL_ELLIPSIS}
</div>
{this.renderActions()}
</Flex>
</Animated.div>
</Draggable>
);
},
handleStop(x, y) {
IncomingCallActionCreators.move(x, y);
},
handleDeclineCall() {
CallActionCreators.stopRinging(this.props.channel.id);
},
handleAcceptWithAudio() {
const {channel} = this.props;
SelectedChannelActionCreators.selectChannel(null, channel.id);
SelectedChannelActionCreators.selectVoiceChannel(null, channel.id);
},
handleAcceptWithVideo() {
AudioActionCreators.setVideoEnabled(true, this.props.channel.id);
this.handleAcceptWithAudio();
},
});
type IncomingCallsState = {
incomingCalls: {[key: string]: {channel: ChannelRecord, x: number, y: number}},
ringing: boolean,
isFocused: boolean,
disableSounds: boolean,
ringingSound: Sound,
};
/**
* Get all incoming calls and render modals
*/
const IncomingCalls = React.createClass({
mixins: [
Flux.LazyStoreListenerMixin(IncomingCallStore, StreamerModeStore, WindowStore, UserSettingsStore),
PureRenderMixin,
],
getInitialState() {
return {
ringingSound: createSound(lodash.random(1, 1000) === 500 ? 'call_ringing_beat' : 'call_ringing'),
...this.getStateFromStores(),
};
},
getStateFromStores() {
return {
incomingCalls: IncomingCallStore.getIncomingCalls(),
ringing: IncomingCallStore.hasIncomingCalls(),
isFocused: WindowStore.isFocused(),
disableSounds: StreamerModeStore.disableSounds,
theme: UserSettingsStore.theme,
};
},
componentWillUpdate(nextProps: any, nextState: IncomingCallsState) {
const {ringing} = nextState;
if (this.state.ringing !== ringing) {
if (ringing) {
this.startRinging();
} else {
this.stopRinging();
}
}
},
render() {
const {incomingCalls, isFocused, theme} = this.state;
return (
<TransitionGroup className={`theme-${theme}`}>
{lodash.map(incomingCalls, props =>
<IncomingCallModal key={props.channel.id} shouldAnimate={isFocused} video={this.props.video} {...props} />
)}
</TransitionGroup>
);
},
// Utils
startRinging() {
if (this.state.disableSounds || NotificationStore.isSoundDisabled('call_ringing')) return;
this.state.ringingSound.loop();
},
stopRinging() {
this.state.ringingSound.stop();
},
});
export default createExperimentContainer(VideoCallExperimentStore, {
[ExperimentTypes.NONE]: props => <IncomingCalls {...props} />,
[ExperimentTypes.DEVELOPER]: {
[ExperimentBuckets.CONTROL]: props => <IncomingCalls video {...props} />,
},
});
// WEBPACK FOOTER //
// ./discord_app/components/IncomingCalls.js