summaryrefslogtreecommitdiffhomepage
path: root/app/components
diff options
context:
space:
mode:
authoranderklander <anderklander@gmail.com>2018-03-19 16:47:27 +0100
committerAndrej Mihajlov <and@mullvad.net>2018-03-26 14:22:11 +0200
commit30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892 (patch)
treeeff01eaf68223fa3a96c6449a3fa10b9b029e126 /app/components
parent962d6e5df554ba67169100b59bada2c754a63713 (diff)
downloadmullvadvpn-30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892.tar.xz
mullvadvpn-30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892.zip
Add animation for sign-in button
Diffstat (limited to 'app/components')
-rw-r--r--app/components/Login.js75
-rw-r--r--app/components/LoginStyles.js6
2 files changed, 59 insertions, 22 deletions
diff --git a/app/components/Login.js b/app/components/Login.js
index fecb5007a9..76e1bf1624 100644
--- a/app/components/Login.js
+++ b/app/components/Login.js
@@ -29,9 +29,11 @@ type State = {
footerHeight: number,
animatedFooterValue: Animated.Value,
animatedDropdownValue: Animated.Value,
- animation: Animated.CompositeAnimation,
- footerAnimationStyle: Animated.Style,
- dropdownAnimationStyle: Animated.Style,
+ animatedLoginButtonValue: Animated.Value,
+ animation: ?Animated.CompositeAnimation,
+ footerAnimationStyle: ?Animated.Style,
+ dropdownAnimationStyle: ?Animated.Style,
+ loginButtonAnimationStyle: ?Animated.Style,
};
export default class Login extends Component<LoginPropTypes, State> {
@@ -42,9 +44,11 @@ export default class Login extends Component<LoginPropTypes, State> {
footerHeight: 0,
animatedFooterValue: Animated.createValue(0),
animatedDropdownValue: Animated.createValue(0),
+ animatedLoginButtonValue: Animated.createValue(0),
animation: null,
footerAnimationStyle: null,
dropdownAnimationStyle: null,
+ loginButtonAnimationStyle: null,
};
constructor(props: LoginPropTypes) {
@@ -58,6 +62,9 @@ export default class Login extends Component<LoginPropTypes, State> {
this.state.footerAnimationStyle = Styles.createAnimatedViewStyle({
transform: [{translateY: this.state.animatedFooterValue }]
});
+ this.state.loginButtonAnimationStyle = Styles.createAnimatedViewStyle({
+ backgroundColor: Animated.interpolate(this.state.animatedLoginButtonValue, [0.0, 1.0], [colors.white, colors.green]),
+ });
}
componentWillReceiveProps(nextProps: LoginPropTypes) {
@@ -106,7 +113,7 @@ export default class Login extends Component<LoginPropTypes, State> {
const relatedTarget = e.relatedTarget;
// restore focus if click happened within dropdown
- if(relatedTarget && this._isWithinDropdown(relatedTarget)) {
+ if(relatedTarget) {
e.target.focus();
return;
}
@@ -122,11 +129,12 @@ export default class Login extends Component<LoginPropTypes, State> {
}
const footerPosition = this._shouldShowFooter(props) ? 0 : this.state.footerHeight;
const dropdownHeight = this._shouldShowAccountHistory(props) ? this.state.dropdownHeight : 0;
- this._setAnimation(this._getFooterAnimation(footerPosition), this._getDropdownAnimation(dropdownHeight));
+ const loginButtonValue = (props.account.accountToken && props.account.accountToken.length) > 0 ? 1 : 0;
+ this._setAnimation(this._getFooterAnimation(footerPosition), this._getDropdownAnimation(dropdownHeight), this._getLoginButtonAnimation(loginButtonValue));
}
- _setAnimation = (footerAnimation: Animated.CompositeAnimation, dropdownAnimation: Animated.CompositeAnimation) => {
- let compositeAnimation = Animated.parallel([ footerAnimation, dropdownAnimation ]);
+ _setAnimation = (footerAnimation: Animated.CompositeAnimation, dropdownAnimation: Animated.CompositeAnimation, loginButtonAnimation: Animated.CompositeAnimation) => {
+ let compositeAnimation = Animated.parallel([ footerAnimation, dropdownAnimation, loginButtonAnimation]);
this.setState({animation: compositeAnimation}, () => {
compositeAnimation.start(() => this.setState({
animation: null
@@ -197,7 +205,7 @@ export default class Login extends Component<LoginPropTypes, State> {
}
}
- _accountInputGroupClass(): Array<Object> {
+ _accountInputGroupStyles(): Array<Object> {
const classes = [styles.account_input_group];
if(this.state.isActive) {
classes.push(styles.account_input_group__active);
@@ -215,7 +223,20 @@ export default class Login extends Component<LoginPropTypes, State> {
return classes;
}
- _accountInputButtonClass(): Array<Object> {
+ _accountInputButtonStyles(): Array<Object> {
+ const { status } = this.props.account;
+ const classes = [styles.account_input_button];
+
+ if(status === 'logging in') {
+ classes.push(styles.account_input_button__invisible);
+ }
+
+ classes.push(this.state.loginButtonAnimationStyle);
+
+ return classes;
+ }
+
+ _accountInputArrowStyles(): Array<Object> {
const { accountToken, status } = this.props.account;
const classes = [styles.account_input_button];
@@ -272,18 +293,30 @@ export default class Login extends Component<LoginPropTypes, State> {
});
}
- _onDropdownLayout = (layout) => {
- this.setState({dropdownHeight: layout.height});
+ _getLoginButtonAnimation(toValue: number){
+ return Animated.timing(this.state.animatedLoginButtonValue, {
+ toValue: toValue,
+ easing: Animated.Easing.Linear(),
+ duration: 250,
+ useNativeDriver: true,
+ });
}
- // returns true if DOM node is within dropdown hierarchy
- _isWithinDropdown(relatedTarget) {
- const dropdownElement = this._accountDropdownElement;
- return dropdownElement && dropdownElement.contains(relatedTarget);
+ _getLoginArrowAnimation(toValue: number){
+ return Animated.timing(this.state.animatedLoginArrowValue, {
+ toValue: toValue,
+ easing: Animated.Easing.Linear(),
+ duration: 250,
+ useNativeDriver: true,
+ });
+ }
+
+ _onDropdownLayout = (layout) => {
+ this.setState({dropdownHeight: layout.height});
}
// container element used for measuring the height of the accounts dropdown
- _accountDropdownElement: ?HTMLElement;
+ _accountDropdownElement: ?React.Node;
_onAccountDropdownContainerRef = ref => this._accountDropdownElement = ref;
_onSelectAccountFromHistory = (accountToken) => {
@@ -303,9 +336,11 @@ export default class Login extends Component<LoginPropTypes, State> {
}
};
+
+
return <View style= {styles.login}>
<Text style={ styles.subtitle }>{ this._formSubtitle() }</Text>
- <View style={ this._accountInputGroupClass() }>
+ <View style={ this._accountInputGroupStyles() }>
<View style={ styles.account_input_backdrop}>
<AccountInput style={styles.account_input_textfield}
type="text"
@@ -320,9 +355,9 @@ export default class Login extends Component<LoginPropTypes, State> {
autoFocus={ true }
ref={ autoFocusOnFailure }
testName='AccountInput'/>
- <Button style={ this._accountInputButtonClass() } onPress={ this._onLogin } testName='account-input-button'>
- <Img style={[ this._accountInputButtonClass() ]} source='icon-arrow' height='16' width='24' tintColor='currentColor' />
- </Button>
+ <Animated.View style={this._accountInputButtonStyles()} onPress={ this._onLogin } testName='account-input-button'>
+ <Img style={this._accountInputArrowStyles()} source='icon-arrow' height='16' width='24' tintColor='currentColor' />
+ </Animated.View>
</View>
<Animated.View style={ this.state.dropdownAnimationStyle }>
<View onLayout={this._onDropdownLayout} ref={ this._onAccountDropdownContainerRef }>
diff --git a/app/components/LoginStyles.js b/app/components/LoginStyles.js
index ea3a2db35c..ed079207bd 100644
--- a/app/components/LoginStyles.js
+++ b/app/components/LoginStyles.js
@@ -10,7 +10,7 @@ export default {
login_footer: {
backgroundColor: colors.darkBlue,
paddingTop: 18,
- paddingBottom:24,
+ paddingBottom:16,
flex: 0,
},
status_icon: {
@@ -64,13 +64,15 @@ export default {
border: 0,
width: 48,
alignItems: 'center',
+ justifyContent: 'center',
color: colors.blue20,
},
account_input_button__active: {
color: colors.white,
- backgroundColor: colors.green,
},
account_input_button__invisible: {
+ color: colors.white,
+ backgroundColor: colors.white,
visibility: 'hidden',
opacity: 0,
},