Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVEMENT] Warn when exporting SRP #3547

Merged
merged 26 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d17376d
Initial UI/UX update for 'Reveal Secret Recovery Phrase' screen
andreahaku Jan 7, 2022
0d23b41
Adds 'Hold to reveal SRP' button to modal with logic
andreahaku Jan 7, 2022
f0312e6
Adds english translations strings support
andreahaku Jan 8, 2022
8266674
adds new UI styling to SRP box
andreahaku Jan 10, 2022
277bc2d
Adds conditional text value for cancel button
andreahaku Jan 10, 2022
3d40b49
Updates UI/UX of app settings>Security & Privacy screen
andreahaku Jan 10, 2022
e6e18a0
typo fix
andreahaku Jan 10, 2022
3886987
Security settings component refactoring
andreahaku Jan 12, 2022
7d05ba9
Adds metrics to components
andreahaku Jan 12, 2022
59ab062
Updates Metrics
andreahaku Jan 14, 2022
c65dec6
fixes typo
andreahaku Jan 31, 2022
8b0b6ee
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 5, 2022
e2158b3
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 7, 2022
e84e41d
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 10, 2022
7d2106f
Fixes SRP modal showing for private key reveal
andreahaku Feb 10, 2022
3c535af
Adds links to blue text
andreahaku Feb 10, 2022
b719ade
Fixes text link position ad missing hyphen
andreahaku Feb 10, 2022
7c2f941
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 22, 2022
1bd0938
Feat/button reveal UI component (#3758)
Cal-L Feb 22, 2022
89cf521
adds animated 'hold to reveal' button
andreahaku Feb 22, 2022
bf00ecc
Fixes reveal button animation on Android
andreahaku Feb 22, 2022
3be13fe
when SRP is copied to clipboard on Android, it doesn't show clipboard…
andreahaku Feb 23, 2022
230e1a7
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 23, 2022
83c12b1
Fixes SRP copied to clipboard message on Android
andreahaku Feb 23, 2022
d50c2a4
Merge branch 'feature/reveal_srp_ui_update' of https://github.com/Met…
andreahaku Feb 23, 2022
ccf547a
Merge branch 'main' into feature/reveal_srp_ui_update
andreahaku Feb 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/components/UI/ButtonReveal/ButtonReveal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import { storiesOf } from '@storybook/react-native';
import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';
import ButtonReveal from './index';

storiesOf('UI / ButtonReveal', module)
.addDecorator((getStory) => getStory())
.add('Default', () => (
<ButtonReveal label={text('label', 'Hold to reveal SRP')} onLongPress={action('onLongPress')} />
));
256 changes: 256 additions & 0 deletions app/components/UI/ButtonReveal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import React from 'react';
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { colors, fontStyles } from '../../../styles/common';
import Svg, { Circle } from 'react-native-svg';
import Animated, {
useSharedValue,
useAnimatedProps,
withTiming,
runOnJS,
useAnimatedStyle,
useAnimatedReaction,
interpolate,
Extrapolate,
runOnUI,
} from 'react-native-reanimated';
const AnimatedCircle = Animated.createAnimatedComponent(Circle);

const radius = 14;
const strokeWidth = 2;
const iconSize = radius - 4;
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
const animationDuration = 1200;

const styles = StyleSheet.create({
container: {
backgroundColor: colors.blue,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 99,
},
progressContainer: {
height: radius * 2,
width: radius * 2,
marginRight: 12,
},
absoluteFillWithCenter: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
},
absoluteFill: {
...StyleSheet.absoluteFillObject,
},
preCompletedContainerStyle: {
...StyleSheet.absoluteFillObject,
borderRadius: radius,
backgroundColor: colors.blue,
},
outerCircle: {
...StyleSheet.absoluteFillObject,
borderRadius: radius,
backgroundColor: colors.white,
},
innerCircle: {
flex: 1,
borderRadius: radius - strokeWidth,
margin: strokeWidth,
backgroundColor: colors.blue,
},
label: {
color: colors.white,
fontSize: 18,
...(fontStyles.normal as any),
},
animatedCircle: {
transform: [
{
rotate: '-90deg',
},
],
},
});

interface Props {
onLongPress: () => void;
label: string;
}

const ButtonReveal = ({ onLongPress, label }: Props) => {
// Values for animating the stroke
const progressOrigin = useSharedValue(innerRadius * 2 * Math.PI);
const progressDestination = useSharedValue(0);
const preCompleteControl = useSharedValue(progressOrigin.value);
// Value for animating the icon & button
const pressControl = useSharedValue(1);
// Value for animating the progress container
const progressContainerOpacity = useSharedValue(1);
// Value for scaling down the progress container
const postCompleteControl = useSharedValue(0);

// Animate SVG via props
const animatedProps = useAnimatedProps(() => ({
strokeDashoffset: preCompleteControl.value,
}));

const resetAnimatedValues = () => {
'worklet';
progressOrigin.value = innerRadius * 2 * Math.PI;
progressDestination.value = 0;
preCompleteControl.value = innerRadius * 2 * Math.PI;
pressControl.value = 1;
postCompleteControl.value = withTiming(
0,
{
duration: 300,
},
() => {
progressContainerOpacity.value = withTiming(1, {
duration: 150,
});
}
);
};

// Reset button to original state
const resetButton = () =>
setTimeout(() => {
runOnUI(resetAnimatedValues)();
}, 1500);

// Post animation from long press
useAnimatedReaction(
() => preCompleteControl.value,
(val) => {
if (val === progressDestination.value) {
// Trigger post long press animation
progressContainerOpacity.value = 0;
postCompleteControl.value = withTiming(1, {
duration: 400,
});
pressControl.value = withTiming(1, {
duration: 400,
});
}
}
);

// Trigger action from long press
useAnimatedReaction(
() => postCompleteControl.value,
(val) => {
if (val === 1) {
// Trigger long press action
runOnJS(onLongPress)();
runOnJS(resetButton)();
}
}
);

// Button is pressed
const triggerPressStart = () => {
const duration = (preCompleteControl.value / progressOrigin.value) * animationDuration;
preCompleteControl.value = withTiming(progressDestination.value, {
duration,
});
pressControl.value = withTiming(0, {
duration: 200,
});
};

// Button is released
const triggerPressEnd = () => {
const duration = ((progressOrigin.value - preCompleteControl.value) / progressOrigin.value) * animationDuration;
preCompleteControl.value = withTiming(progressOrigin.value, {
duration,
});
pressControl.value = withTiming(1, {
duration: 400,
});
};

const outerCircleStyle = useAnimatedStyle(() => ({
transform: [{ scale: interpolate(postCompleteControl.value, [0, 0.5], [1, 0], Extrapolate.CLAMP) }],
}));

const innerCircleStyle = useAnimatedStyle(() => ({
transform: [{ scale: interpolate(postCompleteControl.value, [0, 0.5], [1, 0], Extrapolate.CLAMP) }],
}));

const preCompletedContainerStyle = useAnimatedStyle(() => ({
opacity: progressContainerOpacity.value,
}));

const lockIconStyle = useAnimatedStyle(() => ({
opacity: pressControl.value,
// transform: [{ scale: pressControl.value }],
}));

const checkIconStyle = useAnimatedStyle(() => ({
transform: [{ scale: interpolate(postCompleteControl.value, [0.5, 1], [0, 1], Extrapolate.CLAMP) }],
}));

const containerStyle = useAnimatedStyle(() => ({
transform: [{ scale: interpolate(pressControl.value, [0, 1], [0.97, 1], Extrapolate.CLAMP) }],
}));

const renderPostCompletedContent = () => (
<View style={styles.absoluteFill}>
<Animated.View style={[styles.outerCircle, outerCircleStyle]}>
<Animated.View style={[styles.innerCircle, innerCircleStyle]} />
</Animated.View>
<Animated.View style={[styles.absoluteFillWithCenter, checkIconStyle]}>
<Icon name={'check'} color={colors.white} size={iconSize * 1.5} />
</Animated.View>
</View>
);

const renderPreCompletedContent = () => (
<Animated.View style={[styles.preCompletedContainerStyle, preCompletedContainerStyle]}>
<Animated.View style={[styles.absoluteFillWithCenter, lockIconStyle]}>
<Icon name={'lock'} color={colors.white} size={iconSize} />
</Animated.View>
<Svg style={styles.absoluteFill}>
<Circle
cx={radius}
cy={radius}
r={innerRadius}
stroke={colors.blue600}
strokeWidth={strokeWidth}
strokeLinecap={'round'}
/>
</Svg>
<Svg style={[styles.absoluteFill, styles.animatedCircle]}>
<AnimatedCircle
animatedProps={animatedProps}
cx={radius}
cy={radius}
r={innerRadius}
stroke={colors.white}
strokeWidth={strokeWidth}
strokeLinecap={'round'}
strokeDasharray={`${circumference} ${circumference}`}
/>
</Svg>
</Animated.View>
);

return (
<TouchableOpacity onPressIn={triggerPressStart} onPressOut={triggerPressEnd} activeOpacity={1}>
<Animated.View style={[styles.container, containerStyle]}>
<View style={styles.progressContainer}>
{renderPostCompletedContent()}
{renderPreCompletedContent()}
</View>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</TouchableOpacity>
);
};

export default ButtonReveal;
2 changes: 1 addition & 1 deletion app/components/UI/Drawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Animated, {
set,
Extrapolate,
} from 'react-native-reanimated';
import { onGestureEvent, withSpring, clamp, timing } from 'react-native-redash';
import { onGestureEvent, withSpring, clamp, timing } from 'react-native-redash/src/v1';
import { colors } from '../../../styles/common';
import { useNavigation } from '@react-navigation/native';
const screenWidth = Dimensions.get('window').width;
Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/ReusableModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Animated, {
useCode,
set,
} from 'react-native-reanimated';
import { onGestureEvent, withSpring, clamp, timing } from 'react-native-redash';
import { onGestureEvent, withSpring, clamp, timing } from 'react-native-redash/src/v1';
import styles from './styles';
const screenHeight = Dimensions.get('window').height;

Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/Transactions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class Transactions extends PureComponent {
};

state = {
selectedTx: (new Map(): Map<string, boolean>),
selectedTx: new Map(),
ready: false,
refreshing: false,
cancelIsOpen: false,
Expand Down
Loading