Merged in UPS23-Create-Signup-Screen4-Create-Initial-Profile-Screen (pull request #4)

UPS23 Create Signup Screen4 Create Initial Profile Screen

* UPS23: Added SignupScreen4 + LinkedIn Integration

    - still some bugs when dealing with oauth, not as similar as the twitter api

* UPS23: Camera and Image Picker working

    - still need to test on physical device since emulator will not allow for testing of camera

* UPS23-Fixed Styling for Signup Screen#4

* UPS23- adding package lock file

* UPS23: Disable linked in button

    - currently we are not able to get the information we need back to populate the initial profile form
    - we only have access to v1 apis which will be deprecated in 2 months
    - awaiting access from linkedin's partner program

* UPS23: Change screen routing order

* UPS23: Remove LinkedInData state object

* UPS23: Flatten profileInfo object to seperate state attributes

Approved-by: Omar Mihilmy <mihilmy@gmail.com>
This commit is contained in:
Okechi Onyeje 2019-01-27 02:45:40 +00:00
parent 588f9cc92d
commit f10c5dfbe1
19 changed files with 10008 additions and 2328 deletions

5
.gitignore vendored
View File

@ -29,12 +29,14 @@ build/
.gradle
local.properties
*.iml
.vscode
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
.envrc
# BUCK
buck-out/
@ -54,3 +56,6 @@ buck-out/
# Bundle artifact
*.jsbundle
#Storybook
storybook

View File

@ -138,6 +138,7 @@ android {
dependencies {
compile project(':react-native-app-settings')
compile project(':react-native-image-picker')
compile project(':react-native-contacts')
compile project(':react-native-webview')
compile project(':react-native-vector-icons')

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".MainApplication"

View File

@ -4,6 +4,7 @@ import android.app.Application;
import com.facebook.react.ReactApplication;
import com.krazylabs.OpenAppSettingsPackage;
import com.imagepicker.ImagePickerPackage;
import com.rt2zz.reactnativecontacts.ReactNativeContacts;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
@ -30,6 +31,7 @@ public class MainApplication extends Application implements ReactApplication {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new OpenAppSettingsPackage(),
new ImagePickerPackage(),
new ReactNativeContacts(),
new RNCWebViewPackage(),
new VectorIconsPackage(),

View File

@ -1,6 +1,8 @@
rootProject.name = 'Tagfer'
include ':react-native-app-settings'
project(':react-native-app-settings').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-app-settings/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-contacts'
project(':react-native-contacts').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-contacts/android')
include ':react-native-webview'

View File

@ -55,8 +55,9 @@
CCFF320D8D3B429E98A05055 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC4E3A77FB9C4C96AC304034 /* MaterialCommunityIcons.ttf */; };
D9DE5DDB1B7D4D3A97259D9B /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F025858B6C364BB985AFD241 /* Octicons.ttf */; };
E42DCC4F98F24BC99A74B29B /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 05246261280A4D30B02BFE85 /* SimpleLineIcons.ttf */; };
5732A1A6497B4CAD966524D3 /* libReactNativePermissions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 75B79A57F6EF4FE2A21CEE97 /* libReactNativePermissions.a */; };
864F29A547984CC590F11FA6 /* libRNOpenAppSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E2957596074A7C85333B68 /* libRNOpenAppSettings.a */; };
B4C9A4F1EFED483EBDF40E81 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6ECD8F725BA458AB7A1D23E /* libRNImagePicker.a */; };
992B84F904804663A133A734 /* libReactNativePermissions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D4B30BFC017424A8C7AD988 /* libReactNativePermissions.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -420,10 +421,12 @@
DB6CE8411CF34300A2ED791F /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
E76DD57CF0E846F0B388335E /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
F025858B6C364BB985AFD241 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
E335F09B945C4E2391D1FCD3 /* ReactNativePermissions.xcodeproj */ = {isa = PBXFileReference; name = "ReactNativePermissions.xcodeproj"; path = "../node_modules/react-native-permissions/ios/ReactNativePermissions.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
75B79A57F6EF4FE2A21CEE97 /* libReactNativePermissions.a */ = {isa = PBXFileReference; name = "libReactNativePermissions.a"; path = "libReactNativePermissions.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
92557FC291AB42ADB5028635 /* RNOpenAppSettings.xcodeproj */ = {isa = PBXFileReference; name = "RNOpenAppSettings.xcodeproj"; path = "../node_modules/react-native-app-settings/ios/RNOpenAppSettings.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
E5E2957596074A7C85333B68 /* libRNOpenAppSettings.a */ = {isa = PBXFileReference; name = "libRNOpenAppSettings.a"; path = "libRNOpenAppSettings.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
54F9E00CCF864C30A5B82943 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; name = "RNImagePicker.xcodeproj"; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
D6ECD8F725BA458AB7A1D23E /* libRNImagePicker.a */ = {isa = PBXFileReference; name = "libRNImagePicker.a"; path = "libRNImagePicker.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
CCA4530A7D6748FE88A55EA2 /* ReactNativePermissions.xcodeproj */ = {isa = PBXFileReference; name = "ReactNativePermissions.xcodeproj"; path = "../node_modules/react-native-permissions/ios/ReactNativePermissions.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
8D4B30BFC017424A8C7AD988 /* libReactNativePermissions.a */ = {isa = PBXFileReference; name = "libReactNativePermissions.a"; path = "libReactNativePermissions.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -458,8 +461,9 @@
054B5176D9D541119DF55198 /* libRNVectorIcons.a in Frameworks */,
AD3198E72354473C9CFBB5F9 /* libRNCWebView.a in Frameworks */,
1BF10613A6304DD5B4189358 /* libRCTContacts.a in Frameworks */,
5732A1A6497B4CAD966524D3 /* libReactNativePermissions.a in Frameworks */,
864F29A547984CC590F11FA6 /* libRNOpenAppSettings.a in Frameworks */,
B4C9A4F1EFED483EBDF40E81 /* libRNImagePicker.a in Frameworks */,
992B84F904804663A133A734 /* libReactNativePermissions.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -705,8 +709,9 @@
2DACAA5CB9F345B5888D681E /* RNVectorIcons.xcodeproj */,
A6904FF8C83444048D6F2AF5 /* RNCWebView.xcodeproj */,
90515674F3834203B1DE6531 /* RCTContacts.xcodeproj */,
E335F09B945C4E2391D1FCD3 /* ReactNativePermissions.xcodeproj */,
92557FC291AB42ADB5028635 /* RNOpenAppSettings.xcodeproj */,
54F9E00CCF864C30A5B82943 /* RNImagePicker.xcodeproj */,
CCA4530A7D6748FE88A55EA2 /* ReactNativePermissions.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -1415,8 +1420,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = TagferTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -1453,8 +1459,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = TagferTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -1492,8 +1499,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = Tagfer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -1520,8 +1528,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = Tagfer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -1555,8 +1564,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = "Tagfer-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -1601,8 +1611,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = "Tagfer-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -1646,8 +1657,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = "Tagfer-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@ -1691,8 +1703,9 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-contacts/ios/RCTContacts",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
"$(SRCROOT)/../node_modules/react-native-app-settings/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-permissions/ios/**",
);
INFOPLIST_FILE = "Tagfer-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";

11774
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -18,13 +18,15 @@
"react-native-flash-message": "^0.1.10",
"react-native-gesture-handler": "^1.0.12",
"react-native-masked-text": "^1.9.2",
"react-native-permissions": "^1.1.1",
"react-native-vector-icons": "^4.6.0",
"react-native-webview": "^2.14.3",
"react-navigation": "^3.0.9",
"react-native-image-picker": "^0.28.0",
"react-native-permissions": "^1.1.1",
"realm": "^2.21.1"
},
"devDependencies": {
"@storybook/react-native": "^4.1.6",
"babel-jest": "23.6.0",
"eslint": "^3.19.0",
"eslint-config-rallycoding": "^3.2.0",

View File

@ -1,17 +1,14 @@
/** Contains the base fields for Profile reuse **/
import { AuthSession } from 'expo';
import React, { Component } from 'react';
import { Alert, Animated, Dimensions, ScrollView, Text, View } from 'react-native';
import { Button, Card, CheckBox, Divider, FormInput, Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { profileUpdate, propertyUpdate } from '../../actions';
import ProfileAvatar from '../ProfileAvatar';
import BizCard from '../BizCard';
import { Confirm, FormLabel, FormMessage, Keyword, Spacer, TextButton } from '../common';
import { PROFILE_FORM } from '../FormTypes';
import colors from '../../config/colors.json';
import privateKeys from '../../../private_keys.json';
import { strings } from '../../locales/i18n';
import { toQueryString } from '../../utils/aux';
const SCREEN_WIDTH = Dimensions.get('window').width;
const DOTS_SIZE = ['first', 'second'];
@ -20,15 +17,6 @@ const scrollX = new Animated.Value(0);
let addedExperience = [{ jobTitle: '', jobCompany: '', jobDates: '', jobLocation: '', jobSummary: '' }];
let addedEducation = [{ school: '', degree: '', fieldOfStudy: '', schoolDates: '', educationSummary: '' }];
/**
* Converts an object to a query string.
*/
function toQueryString(params) {
return '?' + Object.entries(params)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
}
class ProfileForm extends Component {
state = {
keyword: '',

View File

@ -1,4 +1,4 @@
const baseurl = 'http://127.0.0.1:3000';
const baseurl = 'http://192.168.1.201:3000';
const endpoints = {
login: {
method: 'POST',
@ -31,6 +31,22 @@ const endpoints = {
findUsersByPhone: {
method: 'POST',
url: `${baseurl}/users/by/phone`
},
signup: {
method: 'PUT',
url: `${baseurl}/auth/signup`
},
retrieveLinkedInInfo: (linkedInQS) => ({
method: 'GET',
url: `${baseurl}/auth/linkedin/get/profile${linkedInQS}`
}),
linkedInToken: {
method: 'GET',
url: `${baseurl}/auth/linkedin/token`
},
linkedInLogin: {
method: 'GET',
url: `${baseurl}/auth/linkedin/login`
}
};

View File

@ -8,9 +8,14 @@ const errors = {
AUTH_PHONE_ALREADY_EXISTS: 'auth/phone-number-already-exists',
AUTH_PHONE_NOT_CACHED: 'auth/phone-number-not-cached',
AUTH_VERIFICATION_CODE_MISMATCH: 'auth/phone-verification-code-mismatch',
AUTH_LINKEDIN_REQUEST_TOKEN_FAILURE: 'auth/linkedin-request-token-failure',
AUTH_LINKEDIN_ACCESS_TOKEN_FAILURE: 'auth/linkedin-access-token-failure',
AUTH_LINKEDIN_PROFILE_REQUEST_FAILURE: 'auth/linkedin-profile-request-failure',
AUTH_LINKEDIN_TOKEN_SCOPE_INVALID_REQUEST_FAILURE: 'auth/linkedin-token-scope-invalid-request-failure',
APP_NETWORK_ERROR: 'app/network-error',
APP_NETWORK_TIMEOUT: 'app/network-timeout',
APP_UNABLE_TO_PARSE_RESPONSE: 'app/unable-to-parse-response',
APP_FIREBASE_DATABASE_ERROR: 'app/firebase-database-error',
MISSING_BODY_ATTRIBUTES: 'request/missing-body-attributes'
};

View File

@ -5,8 +5,10 @@ import SignupScreenOne from '../screens/auth/SignupScreen1';
import SignupScreenTwo from '../screens/auth/SignupScreen2';
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 TwitterWebView from '../screens/auth/TwitterWebView';
import LinkedInWebView from '../screens/auth/LinkedInWebView';
import ForgotPasswordScreen from '../screens/auth/ForgotPasswordScreen';
const MainNavigator = createStackNavigator({
@ -17,6 +19,7 @@ const MainNavigator = createStackNavigator({
signup2: SignupScreenTwo,
signup3a: SignupScreenThreeA,
signup3b: SignupScreenThreeB,
signup4: SignupScreenFour,
signup5: SignupScreenFive
});
@ -26,7 +29,8 @@ const RootNavigator = createStackNavigator(
screen: MainNavigator,
navigationOptions: { header: null }
},
twitterWebView: TwitterWebView
twitterWebView: TwitterWebView,
linkedInWebView: LinkedInWebView
},
{
mode: 'modal'

View File

@ -24,7 +24,7 @@
"sendInvitation": "Send Invitation",
"share": "Share Now",
"skip": "Skip",
"uploadImage": "Upload Image",
"uploadPhoto": "Upload Photo",
"takePhoto": "Take Photo",
"saveAndContinue": "Save and Continue"
},
@ -77,6 +77,14 @@
"lastName_labelText": "Last Name",
"lastName_placeholder": "Last Name",
"linkedIn_import": "Import data from LinkedIn",
"company_number_labelText": "Company Number",
"company_number_placeholder": "0000000000",
"company_title_labelText": "Title Profession",
"company_title_placeholder": "E.g.: CEO, UX Designer",
"company_name_labelText": "Company Name",
"company_name_placeholder": "Your company name",
"company_address_labelText": "Company Email Address",
"company_address_placeholder": "Your company email address",
"location_labelText": "Location",
"location_placeholder": "Santa Clara, CA",
"notes_labelText": "Notes",
@ -568,6 +576,11 @@
"title": "Sign Up",
"urgencyMessage": "Claim your Tagfer ID before it's taken."
},
"defaultProfileCreate": {
"title": "Profile Creation",
"instructions": "The profile you're creating is called Business Profile. You can edit its name and create more profiles either after signing up.",
"no_verification_needed": "(no phone verification needed)"
},
"termsAndConditions": {
"example": "These are the terms and conditions",
"title": "Terms and Conditions"

View File

@ -0,0 +1,31 @@
import React from 'react';
import { Button } from 'react-native';
import { WebView } from 'react-native-webview';
export default class LinkedInWebView extends React.Component {
static navigationOptions = ({ navigation }) => ({
headerLeft: null,
headerRight: <Button title="Done" onPress={() => navigation.goBack()} />
});
/**
* This the bridge connecting the WebView and ReactNative. The data you see here is sent by postMessage from
* within the DOM in the WebView.
*/
onComplete(data) {
const result = JSON.parse(data);
if (result) {
this.props.navigation.state.params.onSuccess(result);
}
this.props.navigation.goBack();
}
render() {
return (
<WebView
source={{ uri: this.props.navigation.state.params.result.uri }}
onMessage={(event) => this.onComplete(event.nativeEvent.data)}
/>
);
}
}

View File

@ -0,0 +1,284 @@
/** Screen for creating default User Profile **/
import React from 'react';
import { Icon, FormInput, Alert, Linking } from 'react-native-elements';
import { Image, KeyboardAvoidingView, Platform, ScrollView, Text, View, Button, TouchableOpacity } from 'react-native';
import ImagePicker from 'react-native-image-picker';
import Permissions from 'react-native-permissions';
import { strings } from '../../../locales/i18n';
import { showFlashErrorMessage } from '../../../utils/errorHandler';
import { fetchAuth } from '../../../utils/fetch';
import endpoints from '../../../config/apiEndpoints';
import { toQueryString } from '../../../utils/aux';
import { FormLabel, Spacer, TextButton } from '../../../components/common';
import styles from './styles';
export default class SignupScreenFour extends React.Component {
static navigationOptions = {
title: strings('userCreateFlow.defaultProfileCreate.title'),
headerStyle: { borderBottomWidth: 0 },
headerTintColor: '#0D497E',
headerRight: <Text style={styles.navigationHeaderRightStyle}>4/6</Text>
};
constructor(props) {
super(props);
this.state = {
base64Img: null,
cameraRollStatus: '',
cameraStatus: '',
loading: false,
jobTitle: '',
companyName: '',
companyAddress: '',
companyNumber: ''
};
this.openLinkedInWebView = this.openLinkedInWebView.bind(this);
this.getLinkedInData = this.getLinkedInData.bind(this);
this.getLinkedInAuthURL = this.getLinkedInAuthURL.bind(this);
this.renderAvatarPhoto = this.renderAvatarPhoto.bind(this);
this.launchPicker = this.launchPicker.bind(this);
}
onSaveButtonPress() {
//this will be replaced with realm once it has been added so that we can persist the user data
//and send the object in this request
this.props.navigation.navigate('signup5', {
jobTitle: this.state.jobTitle,
companyName: this.state.companyName,
companyAddress: this.state.companyAddress,
companyNumber: this.state.companyNumber
});
}
async onImageButtonPress(pickerMethod, pickerTypeState) {
try {
if (this.state[pickerTypeState] !== 'authorized') {
const response = await Permissions.request('photo');
if (response !== 'authorized') {
Alert.alert(strings(`common.error.permissionDenied_camera${pickerTypeState === 'cameraRollStatus' ? 'Roll' : ''}`));
Linking.openURL('app-settings:');
this.setState({ [pickerTypeState]: response });
return;
} else if (response === 'authorized') {
const status = await Permissions.check('photo');
await this.launchPicker(pickerMethod, pickerTypeState, status);
}
}
} catch (error) {
console.log(error);
}
}
// Network request to our API to get an OAuth token
async getLinkedInAuthURL() {
console.log('getting linkedin auth info');
this.setState({ loading: true });
console.log('sending fetch request');
const result = await fetchAuth(endpoints.linkedInLogin);
if (result.error) {
showFlashErrorMessage(result.error);
return null;
}
return result;
}
async getLinkedInData(linkedInAuthData) {
try {
const result = await fetchAuth(endpoints.retrieveLinkedInInfo(toQueryString(linkedInAuthData)));
if (result.error) {
showFlashErrorMessage(result.error);
} else {
//this call will be altered once we gefull linkedin partner api access
//this.setState({ profileInfo: result, loading: false });
}
} catch (error) {
showFlashErrorMessage(error);
this.setState({ loading: false });
}
}
// Navigates to a LinkedIn webview modal, when the auth creds are fetched successfully it will execute onSuccess
async openLinkedInWebView() {
console.log('linkedWebview Func hit');
this.setState({ loading: true });
try {
const result = await this.getLinkedInAuthURL();
this.setState({ loading: false });
console.log('navigating to linkedin webview');
this.props.navigation.navigate('linkedInWebView', { result, onSuccess: this.getLinkedInData });
} catch (error) {
showFlashErrorMessage(error);
this.setState({ loading: false });
}
}
async launchPicker(pickerMethod, pickerTypeState, status) {
await ImagePicker[pickerMethod]({
allowsEditing: true,
mediaTypes: 'photo'
}, (libraryResponse) => {
if (libraryResponse.didCancel) {
console.log('User cancelled image picker');
} else if (libraryResponse.error) {
console.log('ImagePicker Error: ', libraryResponse.error);
} else if (libraryResponse.customButton) {
console.log('User tapped custom button: ', libraryResponse.customButton);
}
this.setState({
base64Img: libraryResponse.data,
[pickerTypeState]: status
});
});
}
renderLinkedInButtons() {
return (
<TextButton
rightText={strings('common.contactFields.linkedIn_import')}
rightTextStyle={{ fontSize: 20, fontWeight: 'normal', color: styles.colors.grey }}
containerStyle={{ marginLeft: -2, marginTop: 15 }}
onPress={this.openLinkedInWebView}
disabled
/>
);
}
/**
* Renders the avatar cirle on profiles
**/
renderAvatarPhoto() {
if (this.state.base64Img) {
return (
<View style={styles.avatarStyle}>
<Image
source={{ uri: this.state.base64Img }}
style={styles.avatarStyle}
/>
{this.renderPhotoButtons()}
</View>
);
}
// No profile Image
return (
<View style={styles.noAvatarStyle}>
<Icon
name='account-circle'
size={150}
color={styles.colors.lightTeal}
/>
{this.renderPhotoButtons()}
</View>
);
}
/**
* Renders the photo buttons
**/
renderPhotoButtons() {
const self = this;
return (
<View style={styles.buttonContainerStyle}>
<TouchableOpacity
onPress={this.onImageButtonPress.bind(self, 'launchImageLibrary', 'cameraRollStatus')}
disabled={this.props.loading}
>
<Text style={styles.textButtonStyle}>{strings('common.buttons.uploadPhoto')}</Text>
</TouchableOpacity>
<Text style={styles.photoButtonsOrTextStyle}>{strings('profile.or')}</Text>
<TouchableOpacity
onPress={this.onImageButtonPress.bind(self, 'launchCamera', 'cameraStatus')}
disabled={this.props.loading}
>
<Text style={styles.textButtonStyle}>{strings('common.buttons.takePhoto')}</Text>
</TouchableOpacity>
</View>
);
}
renderInputComponents(labelString, inputPlaceholderText, onChangeInput) {
return (
<View>
<FormLabel
bold
labelText={labelString}
textStyle={styles.labelStyle}
/>
<FormInput
underlineColorAndroid={styles.colors.greyBlue}
placeholder={inputPlaceholderText}
onChangeText={value => this.setState({ [onChangeInput]: value })}
style={styles.inputTextStyle}
autoCorrect={false}
autoCapitalize='none'
/>
</View>
);
}
renderContent() {
return (
<ScrollView contentContainerStyle={styles.parentContentScrollViewStyle} keyboardShouldPersistTaps="handled">
<View style={{ flex: 1 }}>
{/* INSTRUCTIONS */}
<Text style={styles.instructions}>{strings('userCreateFlow.defaultProfileCreate.instructions')}</Text>
<Spacer />
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
}}
>
{this.renderAvatarPhoto()}
<Spacer />
{this.renderLinkedInButtons()}
{this.renderInputComponents(strings('common.contactFields.company_title_labelText'), strings('common.contactFields.company_title_placeholder'), 'jobTitle')}
{this.renderInputComponents(strings('common.contactFields.company_name_labelText'), strings('common.contactFields.company_name_placeholder'), 'companyName')}
{this.renderInputComponents(strings('common.contactFields.company_address_labelText'), strings('common.contactFields.company_address_placeholder'), 'companyAddress')}
{this.renderInputComponents(strings('common.contactFields.company_number_labelText'), strings('common.contactFields.company_number_placeholder'), 'companyNumber')}
<Text>{strings('userCreateFlow.defaultProfileCreate.no_verification_needed')}</Text>
<Spacer />
</View>
</View>
{/* SAVE AND CONTINUE BUTTON */}
<Button
onPress={this.onSaveButtonPress.bind(this)}
title={strings('common.buttons.saveAndContinue')}
loading={this.state.loading}
style={styles.buttonStyle}
fontSize={20}
/>
</ScrollView>
);
}
/**
* Render method
**/
render() {
if (Platform.OS === 'android') {
return this.renderContent();
}
// IOS
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
keyboardVerticalOffset={90}
behavior="padding"
>
{this.renderContent()}
</KeyboardAvoidingView>
);
}
}

View File

@ -0,0 +1,95 @@
import { SCREEN_WIDTH, colors } from '../../styleVars';
const styles = {
logoContainerStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
logoStyle: {
marginTop: 20,
width: 120,
height: 120,
borderWidth: 1,
borderRadius: 60
},
buttonStyle: {
width: SCREEN_WIDTH * 0.9,
marginTop: 15,
marginBottom: 30,
borderRadius: 5,
backgroundColor: colors.buttonBlue
},
inputTextStyle: {
marginLeft: 15,
color: colors.black,
borderBottomWidth: 1,
borderBottomColor: colors.black,
},
instructions: {
fontSize: 16,
fontFamily: 'Sans Serif',
textAlign: 'left',
color: colors.darkGrey,
marginBottom: 5,
marginHorizontal: 10
},
freeTextStyle: {
left: 20,
marginTop: 10,
fontSize: 18
},
navigationHeaderRightStyle: {
color: colors.middleGrey,
marginRight: 5
},
parentContentScrollViewStyle: {
flexGrow: 1,
backgroundColor: colors.white
},
buttonContainerStyle: {
flex: 2,
marginTop: 15,
flexDirection: 'column',
justifyContent: 'center'
},
photoButtonsOrTextStyle: {
fontSize: 20,
fontWeight: 'bold',
color: colors.lightGrey,
marginTop: 5,
marginBottom: 5,
marginLeft: 15
},
avatarStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginLeft: 25
},
noAvatarStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10
},
textButtonStyle: {
fontSize: 20,
fontWeight: 'bold',
color: colors.labelBlue,
marginTop: 5,
marginBottom: 5,
marginLeft: 0
},
labelStyle: {
fontWeight: 'bold',
color: colors.labelBlue,
marginTop: 5,
marginLeft: 0
},
colors
};
export default styles;

6
src/screens/styleVars.js Normal file
View File

@ -0,0 +1,6 @@
import { Dimensions } from 'react-native';
import colors from '../config/colors.json';
const SCREEN_WIDTH = Dimensions.get('window').width;
module.exports = { SCREEN_WIDTH, colors };

View File

@ -4,5 +4,17 @@ export function isEmail(email) {
}
export function isEmpty(value) {
if (typeof value === 'object') {
return Object.keys(value).length === 0 && value.constructor === Object;
}
return !value;
}
/**
* Converts an object to a query string.
*/
export function toQueryString(params) {
return '?' + Object.entries(params)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
}

View File

@ -40,6 +40,22 @@ export function getErrorObject(error) {
type: 'Authentication Error',
desc: 'Verification code mismatch'
};
case errors.AUTH_LINKEDIN_REQUEST_TOKEN_FAILURE: return {
type: 'Authentication Error',
desc: 'Could not retrieve your login info'
};
case errors.AUTH_LINKEDIN_ACCESS_TOKEN_FAILURE: return {
type: 'Authentication Error',
desc: 'Could not log into your account'
};
case errors.AUTH_LINKEDIN_PROFILE_REQUEST_FAILURE: return {
type: 'Authentication Error',
desc: 'Could not retrieve your profile info'
};
case errors.AUTH_LINKEDIN_TOKEN_SCOPE_INVALID_REQUEST_FAILURE: return {
type: 'Authentication Error',
desc: 'There was a problem connecting to linkedin login, if you have previously authenticated Tagfer with your account please remove the tagfer app from account and reconnect tagfer.'
};
case errors.APP_NETWORK_ERROR: return {
type: 'Network Error',
desc: 'Request failed, check your internet connection.'
@ -52,6 +68,12 @@ export function getErrorObject(error) {
type: 'Network Error',
desc: 'Unable to get the response.'
};
case errors.APP_FIREBASE_DATABASE_ERROR: return {
type: 'Network Error',
desc: 'Unable to save data to our servers.'
};
default: return {
type: 'Error',
desc: 'Oops, an error occured! Please try again.'