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

Show the contract receiving token allowances on the allowance screen #3692

Merged
merged 26 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e8a0acc
show contract receiving token and allow to add a nickname
blackdevelopa Feb 4, 2022
c73c03f
updates on show contract receiving details
blackdevelopa Feb 11, 2022
351c52c
fixed copy feedback
blackdevelopa Feb 11, 2022
6bf45ba
design updates
blackdevelopa Feb 13, 2022
b8ddd36
added text to translation
blackdevelopa Feb 15, 2022
20ac78a
fixed QA issue 1 & 3
blackdevelopa Feb 20, 2022
4ac24c2
QA issue 2 fixed
blackdevelopa Feb 23, 2022
99dbc71
added a check
blackdevelopa Feb 24, 2022
6b7555f
fix issue 4
blackdevelopa Mar 1, 2022
04b2510
design feedback from demo
blackdevelopa Mar 1, 2022
1e64ad3
added longpress to contract address when editing one
blackdevelopa Mar 1, 2022
e766f73
fix bug with displaying empty label, styling transactionreview
blackdevelopa Mar 2, 2022
e670e3c
update on feedback
blackdevelopa Mar 3, 2022
3870262
contract address shows in contact list and not send flow
blackdevelopa Mar 7, 2022
11cb950
fix the delay with updating the sendflow
blackdevelopa Mar 8, 2022
cc7e0a6
before backing seedphrase, the app freezes when address is copied in …
blackdevelopa Mar 9, 2022
e85c3db
creating e2e tests
cortisiko Mar 8, 2022
4c543b5
test changes
cortisiko Mar 9, 2022
2e0ba26
introduced isloading when checking for contract address
blackdevelopa Mar 9, 2022
63c3986
fixed a bug
blackdevelopa Mar 9, 2022
1d5420c
final test changes
cortisiko Mar 9, 2022
86eee5a
close send view.
cortisiko Mar 10, 2022
0309704
broken edge case
blackdevelopa Mar 10, 2022
6a19701
move contract nickname into it's own file.
cortisiko Mar 10, 2022
0cfbebc
fixing test because keyboard issue on CI.
cortisiko Mar 11, 2022
bb98451
Merge branch 'main' into feature/2723-approve-hostname
blackdevelopa Mar 14, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { View } from 'react-native';
import Text from '../../../Base/Text';
import { strings } from '../../../../../locales/i18n';
import AntDesignIcon from 'react-native-vector-icons/AntDesign';

interface HeaderProps {
onUpdateContractNickname: () => void;
nicknameExists: boolean;
headerWrapperStyle?: any;
headerTextStyle?: any;
iconStyle?: any;
}

const Header = (props: HeaderProps) => {
const { onUpdateContractNickname, nicknameExists, headerWrapperStyle, headerTextStyle, iconStyle } = props;
blackdevelopa marked this conversation as resolved.
Show resolved Hide resolved
return (
<View style={headerWrapperStyle}>
<Text bold style={headerTextStyle}>
{nicknameExists ? strings('nickname.edit_nickname') : strings('nickname.add_nickname')}
</Text>
<AntDesignIcon name={'close'} size={20} style={iconStyle} onPress={() => onUpdateContractNickname()} />
</View>
);
};

export default Header;
256 changes: 256 additions & 0 deletions app/components/UI/ApproveTransactionReview/AddNickname/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import React, { useState } from 'react';
import { SafeAreaView, View, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
import AntDesignIcon from 'react-native-vector-icons/AntDesign';
import { colors, fontStyles } from '../../../../styles/common';
import EthereumAddress from '../../EthereumAddress';
import Engine from '../../../../core/Engine';
import AnalyticsV2 from '../../../../util/analyticsV2';
import { toChecksumAddress } from 'ethereumjs-util';
import { connect } from 'react-redux';
import StyledButton from '../../StyledButton';
import Text from '../../../Base/Text';
import InfoModal from '../../Swaps/components/InfoModal';
import { showSimpleNotification } from '../../../../actions/notification';
import Identicon from '../../../UI/Identicon';
import Feather from 'react-native-vector-icons/Feather';
import { strings } from '../../../../../locales/i18n';
import GlobalAlert from '../../../UI/GlobalAlert';
import { showAlert } from '../../../../actions/alert';
import ClipboardManager from '../../../../core/ClipboardManager';
import Header from '../AddNickNameHeader';
import ShowBlockExplorer from '../ShowBlockExplorer';

const styles = StyleSheet.create({
blackdevelopa marked this conversation as resolved.
Show resolved Hide resolved
container: {
flex: 1,
backgroundColor: colors.white,
},
headerWrapper: {
position: 'relative',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginHorizontal: 15,
marginVertical: 5,
paddingVertical: 10,
},
icon: {
position: 'absolute',
right: 0,
padding: 10,
},
headerText: {
color: colors.black,
textAlign: 'center',
fontSize: 15,
},
addressWrapperPrimary: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 10,
},
addressWrapper: {
backgroundColor: colors.blue100,
flexDirection: 'row',
alignItems: 'center',
borderRadius: 40,
paddingVertical: 10,
paddingHorizontal: 15,
width: '90%',
},
address: {
fontSize: 12,
color: colors.grey400,
letterSpacing: 0.8,
marginLeft: 10,
},
label: {
fontSize: 14,
paddingVertical: 12,
color: colors.fontPrimary,
},
input: {
...fontStyles.normal,
fontSize: 12,
borderColor: colors.grey200,
borderRadius: 5,
borderWidth: 2,
padding: 10,
flexDirection: 'row',
alignItems: 'center',
},
bodyWrapper: {
marginHorizontal: 20,
marginBottom: 'auto',
},
updateButton: {
marginHorizontal: 20,
},
addressIdenticon: {
alignItems: 'center',
marginVertical: 10,
},
actionIcon: {
color: colors.blue,
},
});

interface AddNicknameProps {
onUpdateContractNickname: () => void;
contractAddress: string;
network: number;
nicknameExists: boolean;
nickname: string;
addressBook: [];
showModalAlert: (config: any) => void;
networkState: any;
type: string;
}

const getAnalyticsParams = () => {
try {
const { NetworkController } = Engine.context as any;
const { type } = NetworkController?.state?.provider || {};
return {
network_name: type,
};
} catch (error) {
return {};
}
};

const AddNickname = (props: AddNicknameProps) => {
const {
onUpdateContractNickname,
contractAddress,
nicknameExists,
nickname,
showModalAlert,
networkState: {
network,
provider: { type },
},
} = props;

const [newNickname, setNewNickname] = useState(nickname);
const [isBlockExplorerVisible, setIsBlockExplorerVisible] = useState(false);
const [showFullAddress, setShowFullAddress] = useState(false);

const copyContractAddress = async () => {
await ClipboardManager.setString(contractAddress);
showModalAlert({
isVisible: true,
autodismiss: 1500,
content: 'clipboard-alert',
data: { msg: strings('transactions.address_copied_to_clipboard') },
});

AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.CONTRACT_ADDRESS_COPIED, getAnalyticsParams());
};

const saveTokenNickname = () => {
const { AddressBookController } = Engine.context;
if (!newNickname || !contractAddress) return;
AddressBookController.set(toChecksumAddress(contractAddress), newNickname, network);
onUpdateContractNickname();
AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.CONTRACT_ADDRESS_NICKNAME, getAnalyticsParams());
};

const showFullAddressModal = () => {
setShowFullAddress(!showFullAddress);
};

const toggleBlockExplorer = () => setIsBlockExplorerVisible(true);

return (
<SafeAreaView style={styles.container}>
{isBlockExplorerVisible ? (
<ShowBlockExplorer
setIsBlockExplorerVisible={setIsBlockExplorerVisible}
type={type}
contractAddress={contractAddress}
headerWrapperStyle={styles.headerWrapper}
headerTextStyle={styles.headerText}
iconStyle={styles.icon}
/>
) : (
<>
<Header
onUpdateContractNickname={onUpdateContractNickname}
nicknameExists={nicknameExists}
headerWrapperStyle={styles.headerWrapper}
headerTextStyle={styles.headerText}
iconStyle={styles.icon}
/>
<View style={styles.bodyWrapper} testID={'contract-nickname-view'}>
{showFullAddress && (
<InfoModal
isVisible
message={contractAddress}
propagateSwipe={false}
toggleModal={showFullAddressModal}
/>
)}
<View style={styles.addressIdenticon}>
<Identicon address={contractAddress} diameter={25} />
</View>
<Text style={styles.label}>{strings('nickname.address')}</Text>
<View style={styles.addressWrapperPrimary}>
<TouchableOpacity
style={styles.addressWrapper}
onPress={copyContractAddress}
onLongPress={showFullAddressModal}
>
<Feather name="copy" size={18} color={colors.blue} style={styles.actionIcon} />
<EthereumAddress address={contractAddress} type="mid" style={styles.address} />
</TouchableOpacity>
<AntDesignIcon
style={styles.actionIcon}
name="export"
size={22}
onPress={toggleBlockExplorer}
/>
</View>
<Text style={styles.label}>{strings('nickname.name')}</Text>
<TextInput
autoCapitalize={'none'}
autoCorrect={false}
onChangeText={setNewNickname}
placeholder={strings('nickname.name_placeholder')}
placeholderTextColor={colors.grey100}
spellCheck={false}
numberOfLines={1}
style={styles.input}
value={newNickname}
testID={'contract-name-input'}
/>
</View>
<View style={styles.updateButton}>
<StyledButton
type={'confirm'}
disabled={!newNickname}
onPress={saveTokenNickname}
testID={'nickname.save_nickname'}
>
{strings('nickname.save_nickname')}
</StyledButton>
</View>
<GlobalAlert />
</>
)}
</SafeAreaView>
);
};

const mapStateToProps = (state: any) => ({
addressBook: state.engine.backgroundState.AddressBookController.addressBook,
networkState: state.engine.backgroundState.NetworkController,
});

const mapDispatchToProps = (dispatch: any) => ({
showModalAlert: (config) => dispatch(showAlert(config)),
showSimpleNotification: (notification: Notification) => dispatch(showSimpleNotification(notification)),
});

export default connect(mapStateToProps, mapDispatchToProps)(AddNickname);
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import WebviewProgressBar from '../../../UI/WebviewProgressBar';
import { getEtherscanAddressUrl, getEtherscanBaseUrl } from '../../../../util/etherscan';
import { WebView } from 'react-native-webview';
import Text from '../../../Base/Text';
import AntDesignIcon from 'react-native-vector-icons/AntDesign';

const styles = StyleSheet.create({
progressBarWrapper: {
height: 3,
width: '100%',
left: 0,
right: 0,
bottom: 0,
position: 'absolute',
zIndex: 999999,
},
});

interface ShowBlockExplorerProps {
blackdevelopa marked this conversation as resolved.
Show resolved Hide resolved
contractAddress: string;
type: string;
setIsBlockExplorerVisible: (isBlockExplorerVisible: boolean) => void;
headerWrapperStyle?: any;
headerTextStyle?: any;
iconStyle?: any;
}

const ShowBlockExplorer = (props: ShowBlockExplorerProps) => {
const { type, contractAddress, setIsBlockExplorerVisible, headerWrapperStyle, headerTextStyle, iconStyle } = props;
const [loading, setLoading] = useState(0);
const url = getEtherscanAddressUrl(type, contractAddress);
const etherscan_url = getEtherscanBaseUrl(type).replace('https://', '');

const onLoadProgress = ({ nativeEvent: { progress } }: { nativeEvent: { progress: number } }) => {
setLoading(progress);
};

const renderProgressBar = () => (
<View style={styles.progressBarWrapper}>
<WebviewProgressBar progress={loading} />
</View>
);

return (
<>
<View style={headerWrapperStyle}>
<Text bold style={headerTextStyle}>
{etherscan_url}
</Text>
<AntDesignIcon
name={'close'}
size={20}
style={iconStyle}
onPress={() => setIsBlockExplorerVisible(false)}
/>
</View>
<WebView source={{ uri: url }} onLoadProgress={onLoadProgress} />
{renderProgressBar()}
</>
);
};

export default ShowBlockExplorer;
blackdevelopa marked this conversation as resolved.
Show resolved Hide resolved
Loading