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

260 lines
6.9 KiB
JavaScript
Executable file

/* @flow */
import React from 'react';
import lodash from 'lodash';
import classNames from 'classnames';
import Flux from '../lib/flux';
import Store from '../lib/flux/Store';
import Animated from '../lib/animated';
import Keybinds from '../lib/Keybinds';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import TransitionGroup from '../lib/transitions/TransitionGroup';
import {ComponentDispatch} from '../utils/ComponentDispatchUtils';
import UserSettings from './user_settings/UserSettings';
import ChannelSettings from './channel_settings/ChannelSettings';
import GuildSettings from './guild_settings/GuildSettings';
import Verification from './verification/Verification';
import LayerStore from '../stores/LayerStore';
import {SETTINGS_LAYERS_LAYOUT} from '../keybinds/settingsLayers';
import {popLayer} from '../actions/LayerActionCreators';
import {Layers as LayerKeys, ComponentActions} from '../Constants';
import '../styles/layers.styl';
const LayerComponents: {[key: string]: typeof ReactComponent} = {
[LayerKeys.USER_SETTINGS]: UserSettings,
[LayerKeys.CHANNEL_SETTINGS]: ChannelSettings,
[LayerKeys.GUILD_SETTINGS]: GuildSettings,
[LayerKeys.VERIFICATION]: Verification,
};
const MODE_SHOWN = 'SHOWN';
const MODE_HIDDEN = 'HIDDEN';
const SCALE_MIN = 0.93;
const SCALE_DEFAULT = 1;
const SCALE_MAX = 1.1;
const OPACITY_HIDDEN = 0;
const OPACITY_SHOWN = 1;
const SPRING_SETTINGS = {
friction: 10,
tension: 100,
};
type LayerState = {
animating: boolean,
scale: Animated.Value,
opacity: Animated.Value,
};
class Layer extends React.PureComponent {
state: LayerState;
_ref: any;
constructor(props) {
super(props);
let initialScale = SCALE_DEFAULT;
let initialOpacity = OPACITY_SHOWN;
if (props.mode === MODE_HIDDEN) {
initialScale = SCALE_MIN;
initialOpacity = OPACITY_HIDDEN;
}
this.state = {
animating: false,
scale: new Animated.Value(initialScale),
opacity: new Animated.Value(initialOpacity),
};
lodash.bindAll(this, ['setRef']);
}
setRef(ref) {
this._ref = ref;
}
getRef() {
return this._ref || {};
}
componentWillReceiveProps(nextProps) {
const {mode: prevMode} = this.props;
const {mode} = nextProps;
if ((mode === MODE_SHOWN && prevMode === MODE_HIDDEN) || (mode === MODE_HIDDEN && prevMode === MODE_SHOWN)) {
this.setState({animating: true});
}
}
componentDidUpdate(prevProps) {
const {mode} = this.props;
const {mode: prevMode} = prevProps;
if (mode === prevMode) {
return;
}
if (mode === MODE_SHOWN && prevMode === MODE_HIDDEN) {
return this.animateIn();
}
if (mode === MODE_HIDDEN && prevMode === MODE_SHOWN) {
return this.animateUnder();
}
}
componentWillEnter(callback) {
// The default animation state for layers that have been added
const {opacity, scale} = this.state;
scale.setValue(SCALE_MAX);
opacity.setValue(OPACITY_HIDDEN);
this.setState({animating: true}, () => this.animateIn(callback));
}
componentWillLeave(callback) {
this.setState({animating: true}, () => this.animateOut(callback));
}
animateIn(callback) {
Store.pauseEmittingChanges(500);
const {opacity, scale} = this.state;
Animated.parallel([
Animated.spring(opacity, {
toValue: OPACITY_SHOWN,
...SPRING_SETTINGS,
}),
Animated.spring(scale, {
toValue: SCALE_DEFAULT,
...SPRING_SETTINGS,
}),
]).start(() => this.animateComplete(callback));
}
animateOut(callback) {
Store.pauseEmittingChanges(500);
const {opacity, scale} = this.state;
const {onPop} = this.getRef().constructor;
Animated.parallel([
Animated.spring(opacity, {
toValue: OPACITY_HIDDEN,
...SPRING_SETTINGS,
}),
Animated.spring(scale, {
toValue: SCALE_MAX,
...SPRING_SETTINGS,
}),
]).start(() => {
callback();
onPop && onPop();
ComponentDispatch.dispatch(ComponentActions.LAYER_POP_COMPLETE);
});
}
animateUnder() {
Store.pauseEmittingChanges(500);
const {opacity, scale} = this.state;
Animated.parallel([
Animated.spring(opacity, {
toValue: OPACITY_HIDDEN,
...SPRING_SETTINGS,
}),
Animated.spring(scale, {
toValue: SCALE_MIN,
...SPRING_SETTINGS,
}),
]).start(() => this.animateComplete());
}
animateComplete(callback) {
this.setState({animating: false}, callback);
}
render() {
const {animating} = this.state;
const {mode} = this.props;
const style = animating || mode === MODE_HIDDEN ? this.getAnimatedStyle() : null;
const children = React.cloneElement(React.Children.only(this.props.children), {ref: this.setRef});
return (
<Animated.div className={classNames('layer', {animating})} style={style}>
{children}
</Animated.div>
);
}
getAnimatedStyle() {
const {opacity, scale} = this.state;
return {
opacity: opacity,
transform: [{scale}, {translateZ: 0}],
};
}
}
const Layers = React.createClass({
mixins: [PureRenderMixin, Flux.LazyStoreListenerMixin(LayerStore)],
getInitialState() {
return this.getStateFromStores();
},
getStateFromStores() {
return {
layers: LayerStore.getLayers(),
};
},
componentDidMount() {
ComponentDispatch.subscribe(ComponentActions.LAYER_POP_ESCAPE_KEY, popLayer);
},
componentWillUpdate(nextProps, nextState) {
const previousHasLayers = this.state.layers.length > 0;
const nextHasLayers = nextState.layers.length > 0;
if (!previousHasLayers && nextHasLayers) {
Keybinds.disable();
if (nextState.layers[nextState.layers.length - 1] !== LayerKeys.VERIFICATION) {
Keybinds.enableTemp(SETTINGS_LAYERS_LAYOUT);
}
} else if (previousHasLayers && !nextHasLayers) {
Keybinds.disableTemp();
}
},
componentWillUnmount() {
ComponentDispatch.unsubscribe(ComponentActions.LAYER_POP_ESCAPE_KEY, popLayer);
},
renderLayers() {
const {children} = this.props;
const {layers} = this.state;
const {length} = layers;
const components = [];
components.push(
<Layer key="layer-base" mode={length === 0 ? MODE_SHOWN : MODE_HIDDEN}>
{children}
</Layer>
);
layers.forEach((component, i) => components.push(this.renderComponent(component, i, length)));
return components;
},
renderComponent(Component: Function | string, key: number, length: number) {
if (typeof Component === 'string') {
Component = LayerComponents[Component];
}
return (
<Layer key={`layer-${key}`} mode={key === length - 1 ? MODE_SHOWN : MODE_HIDDEN}>
<Component />
</Layer>
);
},
render() {
return (
<TransitionGroup component="div" className={classNames('layers', this.props.className)}>
{this.renderLayers()}
</TransitionGroup>
);
},
});
export default Layers;
// WEBPACK FOOTER //
// ./discord_app/components/Layers.js