mirror of
https://bitbucket.org/tagfer_team/tagfer-app.git
synced 2025-12-25 03:37:41 +00:00
UPS14-SuggestContacts (pull request #1)
This commit is contained in:
parent
f10c5dfbe1
commit
652b74b003
@ -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];
|
||||
|
||||
20
src/components/new/ListActivityIndicator.js
Normal file
20
src/components/new/ListActivityIndicator.js
Normal 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;
|
||||
30
src/components/new/TagferAvatar.js
Normal file
30
src/components/new/TagferAvatar.js
Normal 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;
|
||||
32
src/components/new/TagferCheckbox.js
Normal file
32
src/components/new/TagferCheckbox.js
Normal 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;
|
||||
28
src/components/new/UserListItem.js
Normal file
28
src/components/new/UserListItem.js
Normal 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
|
||||
;
|
||||
@ -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',
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -202,6 +202,8 @@
|
||||
},
|
||||
|
||||
"contact": {
|
||||
"suggestedContacts_title": "Suggested contacts",
|
||||
"suggestedContacts_description": "Link up with Tagfer’s 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": {
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
133
src/screens/auth/SignupScreen6.js
Normal file
133
src/screens/auth/SignupScreen6.js
Normal 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
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user