diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d7255ec..51f6bc0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + getPackages() { new MainReactPackage(), new RNFirebasePackage(), // add/remove these packages as appropriate + new ImagePickerPackage() // <-- add this line + // OR if you want to customize dialog style + new ImagePickerPackage(R.style.my_dialog_style) new RNFirebaseAdMobPackage(), new RNFirebaseAnalyticsPackage(), new RNFirebaseAuthPackage(), diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bc..544d216 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0c..f0e82fb 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0..c136643 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72c..aa56d81 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/build.gradle b/android/build.gradle index 2d1d0d1..4053aaa 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,6 +7,8 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.google.gms:google-services:3.1.1' + classpath 'com.android.tools.build:gradle:2.2.+' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/assets/play/ic_launcher.png b/assets/play/ic_launcher.png new file mode 100644 index 0000000..6dec215 Binary files /dev/null and b/assets/play/ic_launcher.png differ diff --git a/assets/xxhdpi/ic_launcher.png b/assets/xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/assets/xxhdpi/ic_launcher.png differ diff --git a/assets/xxxhdpi/ic_launcher_APP.png b/assets/xxxhdpi/ic_launcher_APP.png new file mode 100644 index 0000000..6298cbe Binary files /dev/null and b/assets/xxxhdpi/ic_launcher_APP.png differ diff --git a/db/favoriteGet.sql b/db/favoriteGet.sql index 57451de..fc97e37 100644 --- a/db/favoriteGet.sql +++ b/db/favoriteGet.sql @@ -1 +1 @@ -SELECT * FROM user_favorites JOIN user_data ON user_data.uid = user_favorites.favoriteuid WHERE userid = ${id}; \ No newline at end of file +SELECT * FROM user_favorites JOIN user_data ON user_data.uid = user_favorites.favoriteuid WHERE userid = ${id} ORDER BY user_favorites.id DESC; \ No newline at end of file diff --git a/index.js b/index.js index 776e1ed..90b03d6 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,9 @@ import { createRootNavigator } from "./router"; import { checkAuth } from "./src/functions/auth"; +import { Provider } from "react-redux"; +import store from "./src/ducks/store"; + export default class App extends React.Component { constructor(props) { super(props); @@ -28,7 +31,11 @@ export default class App extends React.Component { return null; } const Layout = createRootNavigator(signedIn); - return ; + return ( + + + + ); } } diff --git a/package-lock.json b/package-lock.json index fecab0e..66c5bc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8907,6 +8907,11 @@ "prop-types": "15.6.0" } }, + "react-native-image-picker": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.26.7.tgz", + "integrity": "sha1-rS7pV/f2zAE5aJPqA9hMsq2y43Y=" + }, "react-native-iphone-x-helper": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.1.tgz", diff --git a/package.json b/package.json index f45cb37..ca3949a 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "react-native-camera": "^0.12.0", "react-native-easy-grid": "^0.1.15", "react-native-firebase": "^3.1.1", + "react-native-image-picker": "^0.26.7", "react-native-keyboard-aware-scroll-view": "^0.4.1", "react-native-linkedin": "^1.3.1", "react-native-material-textfield": "^0.11.0", @@ -52,6 +53,10 @@ "react-native-svg-animated-linear-gradient": "^0.1.8", "react-native-swiper": "^1.5.13", "react-navigation": "^1.0.0-beta.21", + "react-redux": "^5.0.6", + "redux": "^3.7.2", + "redux-devtools-extension": "^2.13.2", + "redux-promise-middleware": "^5.0.0", "rn-placeholder": "^1.2.0", "styled-components": "^2.2.4" } diff --git a/router.js b/router.js index 2361b45..fd97906 100644 --- a/router.js +++ b/router.js @@ -1,26 +1,43 @@ -import { StackNavigator, TabNavigator } from 'react-navigation'; -import AppOnBoard from './src/components/onBoarding/AppOnBoard/AppOnBoard'; -import SignUpOnBoard from './src/components/onBoarding/SignUpOnBoard/SignUpOnBoard'; -import EmailSignUp from './src/components/onBoarding/auth/signUp/EmailSignUp'; -import Connect from './src/components/connect/Connect'; -import Profile from './src/components/user/profile/Profile'; -import Scan from './src/components/scan/Scan'; -import FavoritesPage from './src/components/favorites/FavoritesPage'; +import { StackNavigator, TabNavigator } from "react-navigation"; +import AppOnBoard from "./src/components/onBoarding/AppOnBoard/AppOnBoard"; +import SignUpOnBoard from "./src/components/onBoarding/SignUpOnBoard/SignUpOnBoard"; +import EmailSignUp from "./src/components/onBoarding/auth/signUp/EmailSignUp"; +import Connect from "./src/components/connect/Connect"; +import Profile from "./src/components/user/profile/Profile"; +import Scan from "./src/components/scan/Scan"; +import FavoritesPage from "./src/components/favorites/FavoritesPage"; -export const ScannedProfile = TabNavigator( +export const ProfilePage = StackNavigator( { Profile: { screen: Profile, - path: '/profile/:user', + }, + Settings: { + screen: Profile, + }, + Info: { + screen: Profile, + }, + }, + { + initialRouteName: "Profile", + } +); + +export const ScannedProfile = TabNavigator( + { + Profile: { + screen: ProfilePage, + path: "/profile/:user", navigationOptions: { - tabBarLabel: 'Profile', + tabBarLabel: "Profile", swipeEnabled: true, }, }, Connect: { screen: Connect, navigationOptions: { - tabBarLabel: 'Connect', + tabBarLabel: "Connect", }, }, }, @@ -29,13 +46,13 @@ export const ScannedProfile = TabNavigator( swipeEnabled: true, tabBarOptions: { showLabel: true, - activeTintColor: 'white', - inactiveTintColor: 'white', - activeBackgroundColor: '#2196f3', - inactiveBackgroundColor: '#1e88e5', + activeTintColor: "white", + inactiveTintColor: "white", + activeBackgroundColor: "#2196f3", + inactiveBackgroundColor: "#1e88e5", }, - initialRouteName: 'Profile', - }, + initialRouteName: "Profile", + } ); export const SignedIn = TabNavigator( @@ -45,17 +62,17 @@ export const SignedIn = TabNavigator( }, Profile: { screen: Profile, - path: '/profile/:user', - initialRouteParams: { user: 'test' }, + path: "/profile/:user", + initialRouteParams: { user: "test" }, navigationOptions: { - tabBarLabel: 'Profile', + tabBarLabel: "Profile", swipeEnabled: true, }, }, Connect: { screen: Connect, navigationOptions: { - tabBarLabel: 'Connect', + tabBarLabel: "Connect", }, }, }, @@ -66,48 +83,49 @@ export const SignedIn = TabNavigator( tabBarOptions: { showLabel: true, indicatorStyle: { - backgroundColor: 'white', + backgroundColor: "white", }, - activeTintColor: 'white', - inactiveTintColor: 'white', - activeBackgroundColor: '#2196f3', - inactiveBackgroundColor: '#1e88e5', + activeTintColor: "white", + inactiveTintColor: "white", + activeBackgroundColor: "#2196f3", + inactiveBackgroundColor: "#1e88e5", }, - initialRouteName: 'Profile', - }, + initialRouteName: "Profile", + } ); -export const createRootNavigator = (signedIn = false) => StackNavigator( - { - SignedIn: { - screen: SignedIn, - path: '/signedIn/:user', - initialRouteName: signedIn ? 'Profile' : 'SignedOut', - }, - SignedOut: { - screen: AppOnBoard, - }, - Scan: { - screen: Scan, - }, - ScannedProfile: { - screen: ScannedProfile, - path: 'profile/:uid', - initialRouteName: 'Profile', - }, - OnBoard: { - screen: AppOnBoard, - }, - SignUpOnBoard: { - screen: SignUpOnBoard, - }, - EmailSignUp: { - screen: EmailSignUp, +export const createRootNavigator = (signedIn = false) => + StackNavigator( + { + SignedIn: { + screen: SignedIn, + path: "/signedIn/:user", + initialRouteName: signedIn ? "Profile" : "SignedOut", + }, + SignedOut: { + screen: AppOnBoard, + }, + Scan: { + screen: Scan, + }, + ScannedProfile: { + screen: ScannedProfile, + path: "profile/:uid", + initialRouteName: "Profile", + }, + OnBoard: { + screen: AppOnBoard, + }, + SignUpOnBoard: { + screen: SignUpOnBoard, + }, + EmailSignUp: { + screen: EmailSignUp, + }, }, - }, - { - headerMode: 'none', - mode: 'modal', - initialRouteName: signedIn ? 'SignedIn' : 'OnBoard', - }, -); + { + headerMode: "none", + mode: "modal", + initialRouteName: signedIn ? "SignedIn" : "OnBoard", + } + ); diff --git a/src/components/connect/Connect.js b/src/components/connect/Connect.js index 9470692..7c04933 100644 --- a/src/components/connect/Connect.js +++ b/src/components/connect/Connect.js @@ -1,28 +1,22 @@ import React, { Component } from "react"; import { Container, Content } from "native-base"; - -import { Vibration, AsyncStorage, StyleSheet, KeyboardAvoidingView } from "react-native"; +import { Vibration, AsyncStorage, KeyboardAvoidingView } from "react-native"; import axios from "axios"; - -import ActionButton from "react-native-action-button"; -import Icon from "react-native-vector-icons/MaterialIcons"; - import ConnectLinkPage from "./connectLink/ConnectLinkPage"; - import AddLinkModal from "./editLinks/AddLink/AddLinkModal"; - import { Fab } from "./fab/Fab"; +import EditModal from "./connectLink/EditModal"; +import { connect } from "react-redux"; -import EditModal from "./editLinks/EditModal/EditModal"; +import { getLinksFromNav, getLinksFromLocal } from "src/ducks/links/actions"; -export default class Connect extends Component { +class Connect extends Component { constructor(props) { super(props); this.state = { editable: false, loading: false, - visible: false, links: [], providers: ["LinkedIn", "Twitter", "Medium", "Phone", "Email"], editableName: "", @@ -45,7 +39,7 @@ export default class Connect extends Component { editableName: val.name, editableLink: val.link, editableId: val.id, - visible: true, + active: false, }); } @@ -55,14 +49,12 @@ export default class Connect extends Component { id: state.editId, }; axios.put("http://172.31.99.35:3001/api/user/connectLink/update", editInfo).then(() => { - console.log("saved"); - this.setState({ visible: false }); + this.setState({ editable: !this.state.editable }); }); } handleDelete(state) { axios.delete(`http://172.31.99.35:3001/api/user/connectLink/delete/${state.id}`).then(result => { - console.log(result); AsyncStorage.setItem("USER_LINKS", JSON.stringify(result.data), () => { this.setState({ links: result.data }); }); @@ -76,23 +68,11 @@ export default class Connect extends Component { } componentDidMount() { + this.props.getLinksFromNav("LinkedIn-Bfnq6wZRo"); this.props.navigation.state.params - ? axios.get(`http://172.31.99.35:3001/api/user/getConnectLinks/${this.props.navigation.state.params.uid}`).then(result => { - this.setState({ - links: result.data, - loading: false, - ownProfile: false, - }); - }) + ? this.props.getLinksFromNav(this.props.navigation.state.params.uid) : AsyncStorage.getItem("USER_LINKS") - ? AsyncStorage.getItem("USER_LINKS").then(res => { - this.setState({ - links: JSON.parse(res), - providers: this.state.providers.filter( - (x, i) => !JSON.parse(res).find(curr => x.toLowerCase() === curr.servicename.toLowerCase()) - ), - }); - }) + ? this.props.getLinksFromLocal() : AsyncStorage.getItem("USER_DATA") .then(result => { axios @@ -106,40 +86,53 @@ export default class Connect extends Component { } render() { + const { + editable, + ownProfile, + handleDelete, + addLink, + editableName, + editableLink, + editableId, + editableColor, + addLinkShow, + } = this.state; + const { providers, links } = this.props.linkProvider; + return ( this.handleDelete(state)} - delete={this.state.handleDelete} - links={this.state.links} - editable={this.state.editable} + delete={handleDelete} + links={links} + editable={editable} editInfo={this.editInfo} handleEdit={this.openEditModal} - ownProfile={this.state.ownProfile} - addLink={() => this.setState({ addLink: !this.state.addLink })} + ownProfile={ownProfile} + addLink={() => this.setState({ addLink: !addLink })} /> {this.state.editableLink ? ( { - this.setState({ visible: false, editableLink: "", editableName: "" }); + this.setState({ editable: false }); }} handleModal={this.openEditModal} - name={this.state.editableName} - link={this.state.editableLink} - id={this.state.editableId} - color={this.state.editableColor} + name={editableName} + link={editableLink} + id={editableId} + color={editableColor} /> ) : null} {this.state.ownProfile && ( this.setState({ editable: !this.state.editable })} + openItems={() => this.setState({ editable: !editable })} editLinks={() => this.setState({ editable: true })} - addLink={() => this.setState({ addLink: !this.state.addLink })} + addLink={() => this.setState({ addLink: !addLink })} editable={this.state.editable} /> )} @@ -155,13 +148,19 @@ export default class Connect extends Component { updateLink={() => { this.getUserData(); }} - providers={this.state.providers} - addLinkShow={this.state.addLinkShow} - visible={this.state.addLink} - links={this.state.links} + providers={this.props.linkProvider.providers} + addLinkShow={addLinkShow} + visible={addLink} + links={this.props.links} /> ); } } + +const mapStateToProps = state => { + return state; +}; + +export default connect(mapStateToProps, { getLinksFromNav, getLinksFromLocal })(Connect); diff --git a/src/components/connect/editLinks/AddLink/slides/ProvidersSlide.js b/src/components/connect/editLinks/AddLink/slides/ProvidersSlide.js index 70ebf02..bd04c61 100644 --- a/src/components/connect/editLinks/AddLink/slides/ProvidersSlide.js +++ b/src/components/connect/editLinks/AddLink/slides/ProvidersSlide.js @@ -1,25 +1,23 @@ import React from "react"; -import { TouchableWithoutFeedback, Vibration } from "react-native"; - import { Slide, ModalHeader, ModalHeaderText, ProviderContainer, Provider, ProviderText } from "../styles"; -import { CloseButton } from "../AddLinkModal"; - import Icon from "react-native-vector-icons/MaterialCommunityIcons"; export const ProvidersSlide = props => { return ( - + Choose a Provider - {props.providers.map((x, i) => ( { + this.state.favorites.map(person => { let obj = { company: person.company, name: person.name, @@ -31,8 +31,6 @@ export default class FavoritesPage extends Component { for (var key in obj) { if (obj[key].slice(0, text.split("").length).toLowerCase() === text.toLowerCase()) { favorites.push(obj); - } else { - console.log("not found"); } } } @@ -57,19 +55,21 @@ export default class FavoritesPage extends Component { this.handleSearch(text)} /> {favs.length ? ( - favs.map((favorite, i) => ( - - )) + favs.map((favorite, i) => { + return ( + + ); + }) ) : ( { if (props.name !== "uhoh") { @@ -37,26 +34,28 @@ export default function IndivFavorite(props) { ); } }}> - - - - - - - - - {props.name} - - - - {props.position} + + + + + + + + + + {props.name} + + + + {props.position} + + + + {props.company} - - - {props.company} - - - + + + ); } diff --git a/src/components/favorites/styles.js b/src/components/favorites/styles.js index a059f39..6a39d41 100644 --- a/src/components/favorites/styles.js +++ b/src/components/favorites/styles.js @@ -1,6 +1,6 @@ import styled from "styled-components/native"; -export const StyledContainer = styled.TouchableOpacity` +export const StyledContainer = styled.TouchableHighlight` width: 95%; height: 120; background-color: #eeeeee; @@ -62,3 +62,15 @@ export const InfoContainer = styled.View` margin-left: 20; padding-right: 5; `; + +export const MarginTopBottom = styled.View` + margin-top: 5 + margin-bottom: 5 +`; + +export const InfoButton = styled.View` + position: absolute; + top: 10; + left: 10; + elevation: 10; +`; diff --git a/src/components/user/profile/Profile.js b/src/components/user/profile/Profile.js index 009e12f..8d28108 100644 --- a/src/components/user/profile/Profile.js +++ b/src/components/user/profile/Profile.js @@ -1,87 +1,91 @@ import React, { Component } from "react"; -import { Container, Content } from "native-base"; - import { AsyncStorage } from "react-native"; import QRCode from "react-native-qrcode"; import ProfileHead from "./profileHead/ProfileHead"; -import axios from "axios"; +import Settings from "../settings/Settings"; + +import { connect } from "react-redux"; +import { getUserInfo } from "src/ducks/user/actions"; -import { CenteredView, QRCodeLoading, Footer, FooterText } from "./styles"; +import { CenteredView, QRCodeLoading, Footer, FooterText, ProfileContainer } from "./styles"; -export default class Profile extends Component { +class Profile extends Component { constructor(props) { super(props); this.state = { loading: true, - profileUid: "", - name: "", - position: "", - profilePicURL: "", - company: "", - userUid: "", - ownProfile: true, saved: false, + settingsVisible: false, }; } componentDidMount() { - const format = data => ({ - name: data.name, - position: data.position, - company: data.company, - profilePicURL: data.profilepic, - loading: false, - profileUid: data.uid, - }); this.props.navigation.state.params - ? axios.get(`http://172.31.99.35:3001/api/user/getInfo/${this.props.navigation.state.params.uid}`).then(({ data }) => { - AsyncStorage.getItem("USER_DATA").then(res => { - this.setState({ - ...format(data), - userUid: JSON.parse(res).uid, - ownProfile: false, - }); - }); - }) - : AsyncStorage.getItem("USER_DATA").then(res => { - const data = JSON.parse(res); - this.setState({ ...format(data), userUid: data.uid }); + ? this.props.getUserInfo(this.props.navigation.state.params.uid, "") + : AsyncStorage.getItem("USER_DATA").then(result => { + this.props.getUserInfo(JSON.parse(result).uid); }); } + + componentWillReceiveProps(newProps) { + this.setState({ ...newProps.profileReducer }); + } + render() { - const { navigate } = this.props.navigation; + const { loading, name, position, company, profilePicURL, profileUid, ownProfile, navigation } = this.props; + const { settingsVisible } = this.state; return ( - - - { - this.setState({ saved: true }); - }} - /> - - {!this.state.loading ? ( - - ) : ( - - )} - - - {!this.state.loading ? ( + + { + this.setState({ settingsVisible: false }); + }} + /> + + { + this.setState({ settingsVisible: true }); + }} + saveItem={() => { + this.setState({ saved: true }); + }} + /> + + {!loading ? ( + + ) : ( + + )} + + + {!loading && !settingsVisible ? (
{ - this.state.ownProfile ? navigate("Scan") : navigate("SignedIn"); - }}> - {this.state.ownProfile ? "Scan" : "Go Back To My Profile"} + ownProfile ? navigation.navigate("Scan") : navigation.navigate("SignedIn"); + }} + ownProfile={this.props.ownProfile}> + {ownProfile ? "Scan" : "Go Back To My Profile"}
) : null} -
+ ); } } + +const mapStateToProps = state => state; + +export default connect(mapStateToProps, { getUserInfo })(Profile); diff --git a/src/components/user/profile/favorites/styles.js b/src/components/user/profile/favorites/styles.js deleted file mode 100644 index b1f1be3..0000000 --- a/src/components/user/profile/favorites/styles.js +++ /dev/null @@ -1,10 +0,0 @@ -import styled from "styled-components/native"; - -export const FavoriteButton = styled.TouchableOpacity` - flex: 1; - justify-content: center; - background-color: ${props => props.color || "#ECEFF1"}; - height: 40; - width: 200; - margin-bottom: 20; -`; diff --git a/src/components/user/profile/profileHead/ProfileFavoriteButton.js b/src/components/user/profile/profileHead/ProfileFavoriteButton.js new file mode 100644 index 0000000..e3cb30b --- /dev/null +++ b/src/components/user/profile/profileHead/ProfileFavoriteButton.js @@ -0,0 +1,50 @@ +import React from "react"; +import { View, AsyncStorage, ActivityIndicator } from "react-native"; +import Icon from "react-native-vector-icons/MaterialCommunityIcons"; + +import { FavoriteButton, FavoriteButtonText } from "./styles"; + +import axios from "axios"; + +let loading = false; + +export const ProfileFavoriteButton = props => { + return ( + { + loading = true; + !props.saved && + axios + .post("http://172.31.99.35:3001/api/user/favorites/save", { + profileUid: props.profileUid, + userUid: props.userUid, + }) + .then(result => { + AsyncStorage.setItem("USER_FAVORITES", JSON.stringify(result.data), () => { + loading = false; + props.saveItem(); + }); + }); + }}> + + {props.saved && ( + + )} + {loading ? ( + + ) : ( + {props.saved ? "Saved" : "Add to Favorites"} + )} + + + ); +}; diff --git a/src/components/user/profile/profileHead/ProfileHead.js b/src/components/user/profile/profileHead/ProfileHead.js index 9a701dc..8cb514f 100644 --- a/src/components/user/profile/profileHead/ProfileHead.js +++ b/src/components/user/profile/profileHead/ProfileHead.js @@ -1,9 +1,8 @@ import React from "react"; -import { View, Text, AsyncStorage, ActivityIndicator } from "react-native"; -import Placeholder from "rn-placeholder"; -import axios from "axios"; +import { View } from "react-native"; import Icon from "react-native-vector-icons/MaterialCommunityIcons"; +import { ProfileFavoriteButton } from "./ProfileFavoriteButton"; import { ProfileImage, @@ -14,62 +13,30 @@ import { JobPositionLoading, JobCompany, JobCompanyLoading, - FavoriteButton, - FavoriteButtonText, CenterView, + InfoButton, } from "./styles"; export default function profileHead(props) { - let loading = false; const image = !props.profilePicURL ? require("./placeholder.png") : { uri: props.profilePicURL }; if (!props.loading) { return ( - - - {props.name} - {props.position} - {props.company} - {!props.ownProfile ? ( - { - loading = true; - !props.saved && - axios - .post("http://172.31.99.35:3001/api/user/favorites/save", { - profileUid: props.profileUid, - userUid: props.userUid, - }) - .then(result => { - AsyncStorage.setItem("USER_FAVORITES", JSON.stringify(result.data), () => { - loading = false; - props.saveItem(); - }); - }); - }}> - - {props.saved && ( - - )} - {loading ? ( - - ) : ( - {props.saved ? "Saved" : "Add to Favorites"} - )} - - - ) : ( - - )} - + + + + + + + {props.name} + {props.position} + {props.company} + {!props.ownProfile ? ( + + ) : ( + + )} + + ); } return ( diff --git a/src/components/user/profile/profileHead/styles.js b/src/components/user/profile/profileHead/styles.js index 241b716..3b4f70b 100644 --- a/src/components/user/profile/profileHead/styles.js +++ b/src/components/user/profile/profileHead/styles.js @@ -1,4 +1,4 @@ -import styled from 'styled-components/native'; +import styled from "styled-components/native"; export const ProfileImage = styled.Image` height: 90; @@ -78,7 +78,7 @@ export const JobCompanyLoading = styled.View` export const FavoriteButton = styled.TouchableOpacity` flex: 1; justify-content: center; - background-color: ${props => (props.saved ? '#4CAF50' : '#0069c0')}; + background-color: ${props => (props.saved ? "#4CAF50" : "#0069c0")}; height: 40; width: 200; margin-bottom: 20; @@ -96,3 +96,10 @@ export const CenterView = styled.View` align-items: center; justify-content: center; `; + +export const InfoButton = styled.TouchableOpacity` + position: absolute; + top: 15; + left: 15; + elevation: 10; +`; diff --git a/src/components/user/profile/styles.js b/src/components/user/profile/styles.js index fc17a78..3bb6d30 100644 --- a/src/components/user/profile/styles.js +++ b/src/components/user/profile/styles.js @@ -1,4 +1,5 @@ import styled from "styled-components/native"; +import { Dimensions } from "react-native"; export const CenteredView = styled.View` align-items: center; @@ -16,8 +17,9 @@ export const Footer = styled.TouchableOpacity` bottom: 0; left: 0; right: 0; - background-color: #2196F3 + background-color: ${props => (props.ownProfile ? "#66BB6A" : "#2196F3")} justify-content: center; + align-items: center height: 10%; `; export const FooterText = styled.Text` @@ -27,11 +29,6 @@ export const FooterText = styled.Text` font-weight: 500; `; -export const FavoriteButton = styled.TouchableOpacity` - flex: 1; - justify-content: center; - background-color: #0069c0; - height: 40; - width: 200; - margin-bottom: 20; +export const ProfileContainer = styled.View` + height: ${Dimensions.get("window").height * 0.885}; `; diff --git a/src/components/user/settings/Settings.js b/src/components/user/settings/Settings.js index e69de29..88349c3 100644 --- a/src/components/user/settings/Settings.js +++ b/src/components/user/settings/Settings.js @@ -0,0 +1,97 @@ +import React, { Component } from "react"; +import { View, TouchableOpacity, Modal, Dimensions } from "react-native"; + +import { connect } from "react-redux"; + +import { closeIconStyle, ProfileImageChange, ProfileImage, TextFieldProps, SaveButton, SaveButtonText } from "./styles"; + +import { TextField } from "react-native-material-textfield"; + +import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; + +import Icon from "react-native-vector-icons/MaterialCommunityIcons"; + +// import { changeName } from "src/ducks/user/settings/actions"; + +export default class Settings extends Component { + constructor(props) { + super(props); + + this.state = { + saved: false, + name: "", + position: "", + company: "", + }; + this.handleTextChange = this.handleTextChange.bind(this); + } + + handleTextChange(key, val) { + this.setState({ key: val }); + } + + render() { + const { profilePic, visible, handleModal, name, position, company } = this.props; + const image = !profilePic ? require("../profile/profileHead/placeholder.png") : { uri: profilePic }; + return ( + + + + + + + + + + + + { + this.handleTextChange("name", text); + }} + value={name} + label={"Name"} + {...TextFieldProps} + /> + { + this.handleTextChange("position", text); + }} + value={position} + label={"Position"} + {...TextFieldProps} + /> + { + this.handleTextChange("company", text); + }} + value={company} + label={"Company"} + {...TextFieldProps} + /> + + + {this.state.saved ? "Saved" : "Save"} + + + + + + ); + } +} + +// const mapStateToProps = state => state; + +// export default connect(mapStateToProps, { changeName })(Settings); diff --git a/src/components/user/settings/styles.js b/src/components/user/settings/styles.js new file mode 100644 index 0000000..85f2d5e --- /dev/null +++ b/src/components/user/settings/styles.js @@ -0,0 +1,61 @@ +import styled from "styled-components/native"; + +export const SettingsTitle = styled.Text` + padding-top: 3% + font-weight: bold + font-size: 26 + text-align: center + color: black +`; + +export const BackButton = styled.TouchableOpacity` + elevation: 8; + background-color: #FAFAFA + width: 80; + height: 80; + position: absolute: +`; + +export const closeIconStyle = { + fontSize: 30, + height: 30, + position: "absolute", + top: 15, + left: 15, +}; + +export const ProfileImageChange = styled.View` + align-items: center; +`; + +export const ProfileImage = styled.Image` + height: 100 + width: 100 + border-radius: 65; + margin-bottom: 5% +`; + +export const TextFieldProps = { + lineWidth: 1, + baseColor: "#29B6F6", + tintColor: "#29B6F6", +}; + +export const SaveButton = styled.TouchableOpacity` + background-color: green + width: 40%; + margin-top: 5% + margin-left: 30% + align-items: center + elevation: 3 + border-radius: 3 + padding-top: 5; + padding-bottom: 5; + +`; + +export const SaveButtonText = styled.Text` + color: white + font-size: 24 + font-weight: bold +`; diff --git a/ducks/authDuck.js b/src/ducks/authentication/actions.js similarity index 100% rename from ducks/authDuck.js rename to src/ducks/authentication/actions.js diff --git a/ducks/messageDuck.js b/src/ducks/authentication/reducer.js similarity index 100% rename from ducks/messageDuck.js rename to src/ducks/authentication/reducer.js diff --git a/ducks/userDuck.js b/src/ducks/favorites/actions.js similarity index 100% rename from ducks/userDuck.js rename to src/ducks/favorites/actions.js diff --git a/src/ducks/favorites/reducer.js b/src/ducks/favorites/reducer.js new file mode 100644 index 0000000..e69de29 diff --git a/src/ducks/links/actions.js b/src/ducks/links/actions.js new file mode 100644 index 0000000..f58d335 --- /dev/null +++ b/src/ducks/links/actions.js @@ -0,0 +1,42 @@ +import { AsyncStorage } from "react-native"; +import axios from "axios"; + +export const GET_LINKS_FROM_NAV = "GET_LINKS_FROM_NAV"; +export const GET_LINKS_FROM_LOCAL = "GET_LINKS_FROM_LOCAL"; +export const ADD_LINK = "ADD_LINK"; +export const UPDATE_LINK = "UPDATE_LINK"; +export const DELETE_LINK = "DELETE_LINK"; + +export function getLinksFromNav(uid) { + return { + type: GET_LINKS_FROM_NAV, + payload: axios.get(`http://172.31.99.35:3001/api/user/getConnectLinks/${uid}`).then(res => res), + }; +} +export function getLinksFromLocal(uid) { + return { + type: GET_LINKS_FROM_LOCAL, + payload: AsyncStorage.getItem("USER_LINKS"), + }; +} + +export function addLink(state) { + return { + type: ADD_LINK, + payload: "", + }; +} + +export function updateLink(state) { + return { + type: UPDATE_LINK, + payload: "", + }; +} + +export function deleteLink(state) { + return { + type: DELETE_LINK, + payload: "", + }; +} diff --git a/src/ducks/links/reducer.js b/src/ducks/links/reducer.js new file mode 100644 index 0000000..025131a --- /dev/null +++ b/src/ducks/links/reducer.js @@ -0,0 +1,55 @@ +import { GET_LINKS_FROM_NAV, GET_LINKS_FROM_LOCAL, ADD_LINK, UPDATE_LINK, DELETE_LINK } from "./actions"; + +import { AsyncStorage } from "react-native"; + +let initialState = { + links: [], + loading: false, + error: false, + providers: ["LinkedIn", "Twitter", "Medium", "Phone", "Email"], + editableName: "", + editableLink: "", + editableColor: "", +}; + +export default function linkReducer(state = initialState, action) { + switch (action.type) { + case GET_LINKS_FROM_NAV + "_PENDING": + return Object.assign({}, state, { loading: true }); + case GET_LINKS_FROM_NAV + "_FULFILLED": + return Object.assign({}, state, { loading: false, links: action.payload.data }); + case GET_LINKS_FROM_NAV + "_REJECTED": + return Object.assign({}, state, { loading: true, error: false }); + case GET_LINKS_FROM_LOCAL + "_PENDING": + return Object.assign({}, state, { loading: true }); + case GET_LINKS_FROM_LOCAL + "_FULFILLED": + let links = JSON.parse(action.payload); + return Object.assign({}, state, { + loading: false, + links: links, + providers: initialState.providers.filter(x => links.find(curr => x.toLowerCase() === curr.servicename.toLowerCase())), + }); + case GET_LINKS_FROM_LOCAL + "_REJECTED": + return Object.assign({}, state, { loading: true, error: false }); + case ADD_LINK + "_PENDING": + return Object.assign({}, state, { loading: true }); + case ADD_LINK + "_FULFILLED": + return Object.assign({}, state, { loading: false }); + case ADD_LINK + "_REJECTED": + return Object.assign({}, state, { loading: true, error: false }); + case UPDATE_LINK + "_PENDING": + return Object.assign({}, state, { loading: true }); + case UPDATE_LINK + "_FULFILLED": + return Object.assign({}, state, { loading: false }); + case UPDATE_LINK + "_REJECTED": + return Object.assign({}, state, { loading: true, error: false }); + case DELETE_LINK + "_PENDING": + return Object.assign({}, state, { loading: true }); + case DELETE_LINK + "_FULFILLED": + return Object.assign({}, state, { loading: false }); + case DELETE_LINK + "_REJECTED": + return Object.assign({}, state, { loading: true, error: false }); + default: + return state; + } +} diff --git a/src/ducks/store.js b/src/ducks/store.js new file mode 100644 index 0000000..741f01f --- /dev/null +++ b/src/ducks/store.js @@ -0,0 +1,11 @@ +import { createStore, combineReducers, applyMiddleware } from "redux"; +import promiseMiddleware from "redux-promise-middleware"; +import { composeWithDevTools } from "redux-devtools-extension"; + +import profileReducer from "./user/reducer"; +import linkReducer from "./links/reducer"; + +export default createStore( + combineReducers({ profileReducer, linkReducer }), + composeWithDevTools(applyMiddleware(promiseMiddleware())) +); diff --git a/src/ducks/user/actions.js b/src/ducks/user/actions.js new file mode 100644 index 0000000..f174253 --- /dev/null +++ b/src/ducks/user/actions.js @@ -0,0 +1,43 @@ +import axios from "axios"; + +export const GET_USER_INFO = "GET_USER_INFO"; +export const CHANGE_EMAIL = "CHANGE_EMAIL"; +export const CHANGE_NAME = "CHANGE_NAME"; +export const CHANGE_PICTURE = "CHANGE_PICTURE"; + +export function getUserInfo(uid) { + return { + type: GET_USER_INFO, + payload: axios.get(`http://172.31.99.35:3001/api/user/getInfo/${uid}`), + }; +} + +export function changeName() { + return { + type: CHANGE_NAME, + payload: axios + .post("/api/user/settings/changeName") + .then(res => res.data) + .catch(console.log), + }; +} + +export function changeEmail() { + return { + type: CHANGE_EMAIL, + payload: axios + .post("/api/user/settings/changeEmail") + .then(res => res.data) + .catch(console.log), + }; +} + +export function changePicture() { + return { + type: CHANGE_PICTURE, + payload: axios + .post("/api/user/settings/changePicture") + .then(res => res.data) + .catch(console.log), + }; +} diff --git a/src/ducks/user/reducer.js b/src/ducks/user/reducer.js new file mode 100644 index 0000000..26b0921 --- /dev/null +++ b/src/ducks/user/reducer.js @@ -0,0 +1,59 @@ +import { AsyncStorage } from "react-native"; + +import { CHANGE_EMAIL, CHANGE_NAME, CHANGE_PICTURE, GET_USER_INFO } from "./actions"; + +let initialState = { + uid: "", + loading: false, + error: false, + name: "", + company: "", + position: "", + profilePicURL: "", + profileUid: "", + ownProfile: true, +}; + +AsyncStorage.getItem("USER_DATA").then(result => { + initialState.uid = JSON.parse(result).uid; +}); + +export default function profileReducer(state = initialState, action) { + switch (action.type) { + case GET_USER_INFO + "_PENDING": + return Object.assign({}, state, { loading: true }); + case GET_USER_INFO + "_FULFILLED": + const { name, position, company, profilepic, uid } = action.payload.data; + return Object.assign({}, state, { + name, + position, + company, + profilePicURL: profilepic, + loading: false, + profileUid: uid, + ownProfile: initialState.uid === uid, + }); + case `${GET_USER_INFO}_REJECTED`: + return Object.assign({}, state, { loading: true, error: true }); + case `${CHANGE_EMAIL}_PENDING`: + return Object.assign({}, state, { loading: true }); + case `${CHANGE_EMAIL}_FULFILLED`: + return Object.assign({}, state, { loading: false }); + case `${CHANGE_EMAIL}_REJECTED`: + return Object.assign({}, state, { loading: true, error: true }); + case `${CHANGE_NAME}_PENDING`: + return Object.assign({}, state, { loading: true }); + case `${CHANGE_NAME}_FULFILLED`: + return Object.assign({}, state, { loading: false }); + case `${CHANGE_NAME}_REJECTED`: + return Object.assign({}, state, { loading: true, error: true }); + case `${CHANGE_PICTURE}_PENDING`: + return Object.assign({}, state, { loading: true }); + case `${CHANGE_PICTURE}_FULFILLED`: + return Object.assign({}, state, { loading: false }); + case `${CHANGE_PICTURE}_REJECTED`: + return Object.assign({}, state, { loading: true, error: true }); + default: + return state; + } +} diff --git a/yarn.lock b/yarn.lock index 900707b..069ae8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1222,7 +1222,7 @@ babel-register@^6.24.1, babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.6.1: +babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -3061,7 +3061,7 @@ hoist-non-react-statics@^1.0.5, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -hoist-non-react-statics@^2.2.0: +hoist-non-react-statics@^2.2.0, hoist-non-react-statics@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" @@ -3198,7 +3198,7 @@ inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -invariant@^2.2.0, invariant@^2.2.2: +invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -3919,6 +3919,10 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +lodash-es@^4.2.0, lodash-es@^4.2.1: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -4034,7 +4038,7 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash@4.17.4, lodash@^4.0.0, lodash@^4.10.1, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.6.1, lodash@~4.17.4: +lodash@4.17.4, lodash@^4.0.0, lodash@^4.10.1, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5037,12 +5041,6 @@ react-clone-referenced-element@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.0.1.tgz#2bba8c69404c5e4a944398600bcc4c941f860682" -react-content-loader@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-1.7.3.tgz#aadef5e86947387981c3920af3152a79aeae7f6e" - dependencies: - babel-runtime "^6.6.1" - react-deep-force-update@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c" @@ -5104,6 +5102,10 @@ react-native-firebase@^3.1.1: postinstall-build "^5.0.1" prop-types "^15.6.0" +react-native-image-picker@^0.26.7: + version "0.26.7" + resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-0.26.7.tgz#ad2ee957f7f6cc01396893ea03d84cb2adb2e376" + react-native-iphone-x-helper@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.1.tgz#950dbdaca66f458689a7e9697c5beb9bd53f98f2" @@ -5247,6 +5249,17 @@ react-proxy@^1.1.7: lodash "^4.6.1" react-deep-force-update "^1.0.0" +react-redux@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946" + dependencies: + hoist-non-react-statics "^2.2.1" + invariant "^2.0.0" + lodash "^4.2.0" + lodash-es "^4.2.0" + loose-envify "^1.1.0" + prop-types "^15.5.10" + react-test-renderer@16.0.0-alpha.12: version "16.0.0-alpha.12" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.0.0-alpha.12.tgz#9e4cc5d8ce8bfca72778340de3e1454b9d6c0cc5" @@ -5348,6 +5361,23 @@ readable-stream@~1.1.8, readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" +redux-devtools-extension@^2.13.2: + version "2.13.2" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" + +redux-promise-middleware@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/redux-promise-middleware/-/redux-promise-middleware-5.0.0.tgz#1656b24f9ac962bf11fbe55ea8360810e18d47a7" + +redux@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.3" + regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" @@ -5546,6 +5576,12 @@ rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" +rn-placeholder@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rn-placeholder/-/rn-placeholder-1.2.0.tgz#d0d7c2fb278698f4101bd0a8a06b14f2147d994c" + dependencies: + prop-types "^15.5.10" + rndm@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/rndm/-/rndm-1.2.0.tgz#f33fe9cfb52bbfd520aa18323bc65db110a1b76c" @@ -5999,6 +6035,10 @@ supports-color@^4.0.0: dependencies: has-flag "^2.0.0" +symbol-observable@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32" + symbol-tree@^3.2.1: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"