UPS14-SuggestContacts (pull request #1)

This commit is contained in:
Omar Mihilmy 2019-01-31 06:48:46 +00:00
parent f10c5dfbe1
commit 652b74b003
10 changed files with 261 additions and 25 deletions

View File

@ -1,8 +1,10 @@
import React from 'react';
import { CheckBox, ListItem, Avatar } from 'react-native-elements';
import { ListItem, Avatar } from 'react-native-elements';
import colors from '../../config/colors.json';
import { isEmpty } from '../../utils/aux';
import TagferCheckbox from '../new/TagferCheckbox.js';
const ContactListItem = ({ contact, selected, onPress }) => {
const { givenName, familyName, phoneNumbers } = contact;
@ -14,7 +16,7 @@ const ContactListItem = ({ contact, selected, onPress }) => {
return (
<ListItem
roundAvatar
avatar={<TagferAvatar firstName={givenName} lastName={familyName} />}
avatar={<TagferInitialsAvatar firstName={givenName} lastName={familyName} />}
title={fullName}
subtitle={phoneNumbers.length !== 0 ? phoneNumbers[0].number : ''}
subtitleStyle={{ fontSize: 12, fontWeight: 'normal' }}
@ -25,23 +27,7 @@ const ContactListItem = ({ contact, selected, onPress }) => {
);
};
const TagferCheckbox = ({ isChecked, onPress }) => (
<CheckBox
iconRight
iconType='ionicon'
checkedIcon='ios-checkmark-circle'
uncheckedIcon='ios-add-circle-outline'
onIconPress={onPress}
onPress={onPress}
uncheckedColor={colors.addGreen}
checkedColor={colors.labelBlue}
checked={isChecked}
size={36}
containerStyle={{ backgroundColor: colors.transparent, borderWidth: 0, margin: 0, padding: 0, marginRight: 0 }}
/>
);
const TagferAvatar = ({ firstName, lastName }) => {
const TagferInitialsAvatar = ({ firstName, lastName }) => {
let initials = '';
if (!isEmpty(firstName)) {
initials += firstName[0];

View File

@ -0,0 +1,20 @@
import React from 'react';
import { ActivityIndicator, View } from 'react-native';
import colors from '../../config/colors.json';
const ListActivityIndicator = () => (
<View style={styles.container}>
<ActivityIndicator size='large' color={colors.labelBlue} />
</View>
);
const styles = {
container: {
flex: 1,
justifyContent: 'center',
marginTop: 30
}
};
export default ListActivityIndicator;

View File

@ -0,0 +1,30 @@
import React from 'react';
import { Avatar } from 'react-native-elements';
import { isEmpty } from '../../utils/aux';
const TagferAvatar = ({ size, photoURL }) => {
const uri = !isEmpty(photoURL) ? { uri: photoURL } : null;
const icon = isEmpty(photoURL) ? { name: 'user', type: 'entypo' } : null;
return (
<Avatar
{...getSize(size)}
rounded
icon={icon}
source={uri}
activeOpacity={0.7}
/>
);
};
function getSize(size) {
switch (size) {
case 'small': return { small: true };
case 'medium': return { medium: true };
case 'large': return { large: true };
case 'xlarge': return { xlarge: true };
default: throw new Error('Unsupported Tagfer Avatar size');
}
}
export default TagferAvatar;

View File

@ -0,0 +1,32 @@
import React from 'react';
import { CheckBox } from 'react-native-elements';
import colors from '../../config/colors.json';
const TagferCheckbox = ({ isChecked, onPress }) => (
<CheckBox
iconRight
iconType='ionicon'
checkedIcon='ios-checkmark-circle'
uncheckedIcon='ios-add-circle-outline'
onIconPress={onPress}
onPress={onPress}
uncheckedColor={colors.addGreen}
checkedColor={colors.labelBlue}
checked={isChecked}
size={36}
containerStyle={styles.checkboxConainer}
/>
);
const styles = {
checkboxConainer: {
backgroundColor: colors.transparent,
borderWidth: 0,
margin: 0,
padding: 0,
marginRight: 0
}
};
export default TagferCheckbox;

View File

@ -0,0 +1,28 @@
import React from 'react';
import { ListItem } from 'react-native-elements';
import TagferAvatar from './TagferAvatar';
import TagferCheckbox from './TagferCheckbox';
import colors from '../../config/colors.json';
const UserListItem = ({ profile, selected, onPress }) => {
const { tagferId, fullName, jobTitle, companyName, photoURL } = profile;
const subtitle = `@${tagferId}\n${jobTitle} at ${companyName}`;
return (
<ListItem
roundAvatar
avatar={<TagferAvatar size='medium' photoURL={photoURL} />}
title={fullName}
subtitle={subtitle}
subtitleNumberOfLines={3}
subtitleStyle={{ fontSize: 12, fontWeight: 'normal' }}
titleStyle={{ fontSize: 16, color: colors.black }}
rightIcon={<TagferCheckbox isChecked={selected} onPress={onPress} />}
containerStyle={{ backgroundColor: colors.white }}
/>);
};
export default UserListItem
;

View File

@ -30,7 +30,11 @@ const endpoints = {
},
findUsersByPhone: {
method: 'POST',
url: `${baseurl}/users/by/phone`
url: `${baseurl}/auth/findUsers/byPhone`
},
suggestProfiles: {
method: 'GET',
url: `${baseurl}/profiles/suggest`
},
signup: {
method: 'PUT',

View File

@ -7,6 +7,7 @@ import SignupScreenThreeA from '../screens/auth/SignupScreen3a';
import SignupScreenThreeB from '../screens/auth/SignupScreen3b';
import SignupScreenFour from '../screens/auth/SignupScreen4/SignupScreen4';
import SignupScreenFive from '../screens/auth/SignupScreen5';
import SignupScreenSix from '../screens/auth/SignupScreen6';
import TwitterWebView from '../screens/auth/TwitterWebView';
import LinkedInWebView from '../screens/auth/LinkedInWebView';
import ForgotPasswordScreen from '../screens/auth/ForgotPasswordScreen';
@ -20,7 +21,8 @@ const MainNavigator = createStackNavigator({
signup3a: SignupScreenThreeA,
signup3b: SignupScreenThreeB,
signup4: SignupScreenFour,
signup5: SignupScreenFive
signup5: SignupScreenFive,
signup6: SignupScreenSix
});
const RootNavigator = createStackNavigator(

View File

@ -202,6 +202,8 @@
},
"contact": {
"suggestedContacts_title": "Suggested contacts",
"suggestedContacts_description": "Link up with Tagfers Power Networkers",
"createScreen_title": "Add Contact",
"editScreen_title": "Edit Contact",
"editScreen_textButton": "Text Contact",
@ -573,7 +575,7 @@
"minPassword": "6+ characters",
"password": "Password",
"password2": "Confirm Password",
"title": "Sign Up",
"title": "Signup",
"urgencyMessage": "Claim your Tagfer ID before it's taken."
},
"defaultProfileCreate": {

View File

@ -53,7 +53,7 @@ export default class SignupScreenFive extends React.Component {
if (selectedContacts.has(index)) {
selectedContacts.delete(index);
} else {
const phoneNumbers = this.state.allContacts[index].phoneNumbers;
const phoneNumbers = state.allContacts[index].phoneNumbers;
selectedContacts.set(index, phoneNumbers);
}
@ -69,7 +69,7 @@ export default class SignupScreenFive extends React.Component {
phoneNumbers.push(...record.map(entry => entry.number));
}
fetchAuth(endpoints.findUsersByPhone, { contacts: phoneNumbers })
fetchAuth(endpoints.findUsersByPhone, { phoneNumbers })
.then(res => console.log(res))
.catch(() => console.log(errors.APP_NETWORK_ERROR));
@ -152,7 +152,6 @@ export default class SignupScreenFive extends React.Component {
onPress={this.onSaveButtonPress.bind(this)}
title={strings('common.buttons.saveAndContinue')}
buttonStyle={styles.buttonStyle}
loading={this.state.loading}
fontSize={20}
iconRight={styles.chevronIconStyle}
/>

View File

@ -0,0 +1,133 @@
import React from 'react';
import { Dimensions, Text, View, FlatList, TouchableOpacity } from 'react-native';
import { Button } from 'react-native-elements';
import { strings } from '../../locales/i18n';
import { fetchAuth } from '../../utils/fetch';
import { showFlashErrorMessage } from '../../utils/errorHandler';
import UserListItem from '../../components/new/UserListItem';
import ListActivityIndicator from '../../components/new/ListActivityIndicator';
import colors from '../../config/colors.json';
import errors from '../../config/errors';
import endpoints from '../../config/apiEndpoints';
export default class SignupScreenSix extends React.Component {
static navigationOptions = {
title: strings('contact.suggestedContacts_title'),
headerStyle: { borderBottomWidth: 0, backgroundColor: colors.offWhite },
headerTintColor: '#0D497E',
headerRight: <Text style={{ color: colors.middleGrey, marginRight: 5 }}>6/6</Text>
};
constructor(props) {
super(props);
this.state = {
users: [],
selectedUsers: new Map()
};
this.onLoad();
}
// Load suggested contacts
onLoad() {
fetchAuth(endpoints.suggestProfiles)
.then(result => this.setState({ users: result.profiles }))
.catch(() => showFlashErrorMessage(errors.APP_NETWORK_ERROR));
}
// Add user to selected map
onSelectUser(index) {
this.setState((state) => {
const selectedUsers = new Map(state.selectedUsers);
if (selectedUsers.has(index)) {
selectedUsers.delete(index);
} else {
selectedUsers.set(index, state.users[index].tagferId);
}
return { selectedUsers };
});
}
// Implement the signup functionality
// TODO: Add all the Realm handlers to handle saving the data
onSignup() {
console.log('Unimplemented Signup');
console.log([...this.state.selectedUsers.values()]);
}
// Render a single entry in the list
renderItem(profile, index) {
const selected = this.state.selectedUsers.has(index);
return <UserListItem profile={profile} selected={selected} onPress={() => this.onSelectUser(index)} />;
}
render() {
return (
<View style={styles.container}>
{/* INSTRUCTIONS + SKIP BUTTON */}
<InstructionsSection onSkip={this.onSignup} />
{/* SUGGESTED CONTACTS */}
<FlatList
data={this.state.users}
extraData={this.state}
renderItem={({ item, index }) => this.renderItem(item, index)}
keyExtractor={(item) => item.tagferId}
style={{ flex: 1, marginHorizontal: 10 }}
ListEmptyComponent={<ListActivityIndicator />}
/>
{/* SIGNUP BUTTON */}
<Button
onPress={this.onSignup.bind(this)}
title={strings('userCreateFlow.signUp.title')}
buttonStyle={styles.buttonStyle}
loading={this.state.loading}
fontSize={20}
/>
</View>
);
}
}
const InstructionsSection = ({ onSkip }) => (
<View style={styles.instructionsContainer}>
<Text style={styles.instructionsText}>{strings('contact.suggestedContacts_description')}</Text>
<TouchableOpacity style={{ marginRight: 5 }} onPress={onSkip}>
<Text style={styles.skipButton}>SKIP</Text>
</TouchableOpacity>
</View>
);
const SCREEN_WIDTH = Dimensions.get('window').width;
const styles = {
container: {
flex: 1,
backgroundColor: colors.offWhite
},
instructionsContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 10,
marginBottom: 15
},
instructionsText: {
fontSize: 14,
color: colors.darkGrey,
marginLeft: 20
},
skipButton: {
fontSize: 12,
color: colors.darkGrey
},
buttonStyle: {
width: SCREEN_WIDTH * 0.9,
marginTop: 15,
marginBottom: 30,
borderRadius: 5,
backgroundColor: colors.buttonBlue
}
};