diff --git a/App.js b/App.js index 8d541bb..86f0a66 100644 --- a/App.js +++ b/App.js @@ -1,6 +1,8 @@ import React from "react"; import { Asset, AppLoading, Font, Updates } from "expo"; -import { Platform } from "react-native"; +import { AsyncStorage, Alert, Platform, View } from "react-native"; +import ApolloClient from "apollo-boost"; +import { ApolloProvider } from "react-apollo"; import { Ionicons } from "@expo/vector-icons"; import { loadSavedTalksAsync } from "./src/utils/storage"; import { SafeAreaView } from "react-navigation"; @@ -9,6 +11,10 @@ if (Platform.OS === "android") { SafeAreaView.setStatusBarHeight(0); } +const client = new ApolloClient({ + uri: "https://www.react-europe.org/gql" +}); + import Navigation from "./src/Navigation"; export default class App extends React.Component { @@ -84,6 +90,10 @@ export default class App extends React.Component { ); } - return ; + return ( + + + + ); } } diff --git a/package.json b/package.json index c701553..667f5f0 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,13 @@ "lint": "eslint . --ext .js --quiet" }, "dependencies": { + "apollo-boost": "^0.1.6", "crypto-js": "^3.1.9-1", "expo": "^27.0.0", "fbemitter": "^2.1.1", "global": "^4.3.2", + "graphql": "^0.13.2", + "graphql-tag": "^2.9.2", "hoist-non-react-statics": "^2.3.1", "lodash": "^4.17.4", "moment": "2.21.0", @@ -21,6 +24,7 @@ "path": "^0.12.7", "prettier": "^1.11.1", "react": "16.3.1", + "react-apollo": "^2.1.4", "react-native": "https://github.com/expo/react-native/archive/sdk-27.0.0.tar.gz", "react-native-animatable": "^1.2.4", "react-native-fade-in-image": "1.3.0", diff --git a/src/Navigation.js b/src/Navigation.js index 6616d9b..a8b289b 100644 --- a/src/Navigation.js +++ b/src/Navigation.js @@ -1,14 +1,14 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; +import PropTypes from "prop-types"; import { TabRouter, TabNavigator, StackNavigator, createNavigator, - createNavigationContainer, -} from 'react-navigation'; -import withCachedChildNavigation from 'react-navigation/src/withCachedChildNavigation'; -import SceneView from 'react-navigation/src/views/SceneView'; + createNavigationContainer +} from "react-navigation"; +import withCachedChildNavigation from "react-navigation/src/withCachedChildNavigation"; +import SceneView from "react-navigation/src/views/SceneView"; import { BackHandler, Platform, @@ -16,54 +16,51 @@ import { StyleSheet, StatusBar, View -} from 'react-native'; -import { Constants } from 'expo'; -import { TabViewAnimated } from 'react-native-tab-view'; -import { - DrawerLayoutAndroid, - RectButton, -} from 'react-native-gesture-handler'; -import DrawerLayout from 'react-native-gesture-handler/DrawerLayout'; -import ResourceSavingContainer from 'react-native-resource-saving-container'; -import hoistStatics from 'hoist-non-react-statics'; - -import { Colors, FontSizes, Layout } from './constants'; -import Screens from './screens'; -import TabBarBottom from './components/TabBarBottom'; -import CachedImage from './components/CachedImage'; -import { SemiBoldText } from './components/StyledText'; -import _ from 'lodash'; -import Schedule from './data/schedule.json'; -import moment from 'moment'; - -import QRScannerModalNavigation from './screens/QRScreens/Identify'; -import QRCheckinScannerModalNavigation from './screens/QRScreens/CheckIn'; -import QRContactScannerModalNavigation from './screens/QRScreens/Contact'; +} from "react-native"; +import { Constants } from "expo"; +import { TabViewAnimated } from "react-native-tab-view"; +import { DrawerLayoutAndroid, RectButton } from "react-native-gesture-handler"; +import DrawerLayout from "react-native-gesture-handler/DrawerLayout"; +import ResourceSavingContainer from "react-native-resource-saving-container"; +import hoistStatics from "hoist-non-react-statics"; + +import { Colors, FontSizes, Layout } from "./constants"; +import Screens from "./screens"; +import TabBarBottom from "./components/TabBarBottom"; +import CachedImage from "./components/CachedImage"; +import { SemiBoldText } from "./components/StyledText"; +import _ from "lodash"; +import Schedule from "./data/schedule.json"; +import moment from "moment"; + +import QRScannerModalNavigation from "./screens/QRScreens/Identify"; +import QRCheckinScannerModalNavigation from "./screens/QRScreens/CheckIn"; +import QRContactScannerModalNavigation from "./screens/QRScreens/Contact"; const DrawerComponent = - Platform.OS === 'android' ? DrawerLayoutAndroid : DrawerLayout; + Platform.OS === "android" ? DrawerLayoutAndroid : DrawerLayout; const FullSchedule = Schedule.events[0].groupedSchedule; let navSchedule = {}; -_.each(FullSchedule, (day) => { +_.each(FullSchedule, day => { navSchedule[day.title] = { screen: Screens.ScheduleDay({ day: day.title, - date: moment(new Date(day.date)).format('D'), - }), + date: moment(new Date(day.date)).format("D") + }) }; }); const ScheduleNavigation = TabNavigator(navSchedule, { - initialRouteName: moment().format('dddd'), + initialRouteName: moment().format("dddd"), lazy: true, swipeEnabled: false, animationEnabled: false, tabBarComponent: TabBarBottom, - tabBarPosition: 'bottom', + tabBarPosition: "bottom", tabBarOptions: { - style: { backgroundColor: '#333' }, - activeTintColor: '#fff', - }, + style: { backgroundColor: "#333" }, + activeTintColor: "#fff" + } }); export function connectDrawerButton(WrappedComponent) { @@ -81,7 +78,7 @@ export function connectDrawerButton(WrappedComponent) { ConnectedDrawerButton.contextTypes = { openDrawer: PropTypes.func, closeDrawer: PropTypes.func, - toggleDrawer: PropTypes.func, + toggleDrawer: PropTypes.func }; return hoistStatics(ConnectedDrawerButton, WrappedComponent); @@ -89,15 +86,15 @@ export function connectDrawerButton(WrappedComponent) { const DefaultStackConfig = { cardStyle: { - backgroundColor: '#fafafa', - }, + backgroundColor: "#fafafa" + } }; const SpeakersNavigation = StackNavigator( { SpeakerList: { - screen: Screens.Speakers, - }, + screen: Screens.Speakers + } }, DefaultStackConfig ); @@ -105,8 +102,17 @@ const SpeakersNavigation = StackNavigator( const CrewNavigation = StackNavigator( { CrewList: { - screen: Screens.Crew, - }, + screen: Screens.Crew + } + }, + DefaultStackConfig +); + +const AttendeesNavigation = StackNavigator( + { + AttendeeList: { + screen: Screens.Attendees + } }, DefaultStackConfig ); @@ -114,8 +120,8 @@ const CrewNavigation = StackNavigator( const SponsorNavigation = StackNavigator( { SponsorList: { - screen: Screens.Sponsors, - }, + screen: Screens.Sponsors + } }, DefaultStackConfig ); @@ -123,8 +129,8 @@ const SponsorNavigation = StackNavigator( const StaffCheckinListsNavigation = StackNavigator( { StaffCheckinListsList: { - screen: Screens.StaffCheckinLists, - }, + screen: Screens.StaffCheckinLists + } }, DefaultStackConfig ); @@ -133,11 +139,12 @@ const DrawerRouteConfig = { Home: { screen: Screens.Home }, Schedule: { screen: ScheduleNavigation }, Speakers: { screen: SpeakersNavigation }, + Attendees: { screen: AttendeesNavigation }, Crew: { screen: CrewNavigation }, Sponsors: { screen: SponsorNavigation }, Profile: { screen: Screens.Profile }, Contacts: { screen: Screens.Contacts }, - StaffCheckinLists: { screen: StaffCheckinListsNavigation }, + StaffCheckinLists: { screen: StaffCheckinListsNavigation } }; const DrawerRouter = TabRouter(DrawerRouteConfig); @@ -145,7 +152,7 @@ const DrawerRouter = TabRouter(DrawerRouteConfig); class DrawerScene extends React.PureComponent { state = { visible: true, - me: null, + me: null }; setVisible = visible => { @@ -161,8 +168,9 @@ class DrawerScene extends React.PureComponent { return ( + style={{ flex: 1, overflow: "hidden" }} + visible={Platform.OS === "android" ? this.state.visible : true} + > { @@ -232,9 +240,8 @@ class DrawerView extends React.Component { const nextIndex = nextProps.navigation.state.index; const nextRoute = nextProps.navigation.state.routes[nextIndex]; - if (!this.state.loaded.includes(nextProps.navigation.state.index)) { - this.setState({loaded: [...this.state.loaded, nextIndex]}); + this.setState({ loaded: [...this.state.loaded, nextIndex] }); } if (currentRoute.key !== nextRoute.key) { @@ -278,15 +285,17 @@ class DrawerView extends React.Component { drawerPosition={DrawerComponent.positions.Left} drawerType="front" drawerBackgroundColor="#333333" - renderNavigationView={this._renderNavigationView}> + renderNavigationView={this._renderNavigationView} + > + Platform.OS === "android" ? Constants.statusBarHeight : 0 + }} + > @@ -320,34 +329,35 @@ class DrawerView extends React.Component { + alignItems: "center", + justifyContent: "center", + paddingTop: Layout.notchHeight + 20 + } + ]} + > @@ -355,18 +365,19 @@ class DrawerView extends React.Component { {/* make sure the buttons here are in the same order as in route config */} {this._renderButtons([ - { route: 'Home', title: 'Home' }, - { route: 'Schedule', title: 'Schedule' }, - { route: 'Speakers', title: 'Speakers' }, - { route: 'Crew', title: 'Crew' }, - { route: 'Sponsors', title: 'Sponsors' }, - { route: 'Profile', title: 'Profile' }, - { route: 'Contacts', title: 'Contacts' }, + { route: "Home", title: "Home" }, + { route: "Schedule", title: "Schedule" }, + { route: "Speakers", title: "Speakers" }, + { route: "Attendees", title: "Attendees" }, + { route: "Crew", title: "Crew" }, + { route: "Sponsors", title: "Sponsors" }, + { route: "Profile", title: "Profile" }, + { route: "Contacts", title: "Contacts" }, { - route: 'StaffCheckinLists', - title: 'StaffCheckinLists', - hidden: true, - }, + route: "StaffCheckinLists", + title: "StaffCheckinLists", + hidden: true + } ])} @@ -382,7 +393,8 @@ class DrawerView extends React.Component { this._navigateToScreen(i)} - selected={selectedIndex === i}> + selected={selectedIndex === i} + > {config.title} ) @@ -416,7 +428,7 @@ const DrawerNavigation = createNavigationContainer( class DrawerButton extends React.Component { state = { - me: null, + me: null }; render() { return ( @@ -424,16 +436,18 @@ class DrawerButton extends React.Component { onPress={this.props.onPress} style={{ backgroundColor: this.props.selected - ? 'rgba(255,255,255,0.1)' - : '#333333', - }}> + ? "rgba(255,255,255,0.1)" + : "#333333" + }} + > + justifyContent: "center", + paddingHorizontal: 5 + }} + > {this.props.children.toUpperCase()} @@ -445,32 +459,33 @@ class DrawerButton extends React.Component { const styles = StyleSheet.create({ container: { - flex: 1, + flex: 1 }, drawerButtonText: { - color: '#fff', + color: "#fff", fontSize: FontSizes.normalButton, - padding: 10, + padding: 10 }, drawerButtons: { - paddingTop: 0, + paddingTop: 0 }, - drawerNavigationContainer: {}, + drawerNavigationContainer: {} }); export default StackNavigator( { Primary: { screen: DrawerNavigation }, Details: { screen: Screens.Details }, + AttendeeDetails: { screen: Screens.AttendeeDetails }, TicketInstructions: { screen: Screens.TicketInstructions }, CheckedInAttendeeInfo: { screen: Screens.CheckedInAttendeeInfo }, QRScanner: { screen: QRScannerModalNavigation }, QRCheckinScanner: { screen: QRCheckinScannerModalNavigation }, - QRContactScanner: { screen: QRContactScannerModalNavigation }, + QRContactScanner: { screen: QRContactScannerModalNavigation } }, { ...DefaultStackConfig, - headerMode: 'none', - mode: 'modal', + headerMode: "none", + mode: "modal" } ); diff --git a/src/components/SearchAttendeeListItem.js b/src/components/SearchAttendeeListItem.js new file mode 100644 index 0000000..f76417a --- /dev/null +++ b/src/components/SearchAttendeeListItem.js @@ -0,0 +1,59 @@ +import React from "react"; +import { Image, Platform, StyleSheet, View, Linking } from "react-native"; +import { Svg, FileSystem, WebBrowser } from "expo"; +import { RectButton } from "react-native-gesture-handler"; +import FadeIn from "react-native-fade-in-image"; +import { withNavigation } from "react-navigation"; +import GravatarImage from "../components/GravatarImage"; +import SaveIconWhenSaved from "./SaveIconWhenSaved"; +import { BoldText, RegularText, SemiBoldText } from "./StyledText"; +import { conferenceHasEnded, getSpeakerAvatarURL } from "../utils"; +import { Colors, FontSizes } from "../constants"; + +import { + Card, + CardActions, + CardContent, + Title, + Paragraph +} from "react-native-paper"; + +//@withNavigation +export default class SearchAttendeeListItem extends React.Component { + render() { + const { contact, onPress } = this.props; + const { firstName, lastName, email } = contact; + + return ( + onPress(contact)} + activeOpacity={0.05} + style={{ flex: 1, backgroundColor: "#fff" }} + > + + + + {firstName + " " + lastName} + + + + ); + } +} + +const styles = StyleSheet.create({ + avatarImage: { + width: 64, + height: 64, + borderRadius: 32, + marginLeft: 8, + marginTop: 8 + }, + listItemTitle: { + flex: 1, + height: 64, + justifyContent: "center", + marginLeft: 8, + marginTop: 8 + } +}); diff --git a/src/components/SearchAttendees.js b/src/components/SearchAttendees.js new file mode 100644 index 0000000..8ba6064 --- /dev/null +++ b/src/components/SearchAttendees.js @@ -0,0 +1,124 @@ +import React from "react"; +import { + Image, + Platform, + StyleSheet, + View, + AsyncStorage, + TextInput, + FlatList +} from "react-native"; +import moment from "moment-timezone"; +import { ApolloConsumer } from "react-apollo"; +import gql from "graphql-tag"; +import _ from "lodash"; +import { Searchbar } from "react-native-paper"; +import { BoldText, RegularText, SemiBoldText } from "./StyledText"; +import SearchAttendeeListItem from "./SearchAttendeeListItem"; +import { Colors, FontSizes } from "../constants"; + +export default class SearchAttendeesInput extends React.Component { + constructor(props) { + super(props); + this.onChangeTextDelayed = _.debounce(this.onChangeText, 500); + this.state = { + text: "", + attendees: [], + loading: false + }; + } + + async onChangeText(text, client) { + const { uuid } = this.props; + if (!this.state.loading) { + this.setState({ loading: true }); + + try { + const { data } = await client.query({ + query: ATTENDEE_SEARCH_QUERY, + variables: { + slug: "reacteurope-2018", + q: text, + uuid + } + }); + + const event = + data && data.events && data.events.length > 0 && data.events[0]; + + let attendees; + if (!event) { + attendees = []; + } else { + attendees = event.attendees ? event.attendees : []; + } + this.setState({ attendees, loading: false }); + } catch (err) { + this.setState({ loading: false }); + } + } + } + + render() { + const { attendees } = this.state; + const { onPressAttendee } = this.props; + return ( + + {client => ( + + + + Search Attendees + + this.onChangeTextDelayed(query, client)} + /> + + + String(item.id)} + renderItem={({ item, index }) => { + return ( + { + onPressAttendee(attendee); + }} + /> + ); + }} + /> + + )} + + ); + } +} + +// +const ATTENDEE_SEARCH_QUERY = gql` + query attendeeSearch($slug: String, $q: String!, $uuid: String!) { + events(slug: $slug) { + attendees(q: $q, uuid: $uuid) { + id + lastName + email + firstName + answers { + id + value + question { + id + title + } + } + } + } + } +`; diff --git a/src/screens/AttendeeDetails.js b/src/screens/AttendeeDetails.js new file mode 100644 index 0000000..ae1a300 --- /dev/null +++ b/src/screens/AttendeeDetails.js @@ -0,0 +1,238 @@ +import React from "react"; +import { + Animated, + Platform, + StyleSheet, + TouchableOpacity, + Text, + ScrollView, + View, + Linking +} from "react-native"; +import { Constants, WebBrowser } from "expo"; +import FadeIn from "react-native-fade-in-image"; +import { View as AnimatableView } from "react-native-animatable"; +import AnimatedScrollView from "../components/AnimatedScrollView"; +import NavigationBar from "../components/NavigationBar"; +import { Colors, FontSizes, Layout } from "../constants"; +import { RegularText, SemiBoldText } from "../components/StyledText"; +import GravatarImage from "../components/GravatarImage"; +import CloseButton from "../components/CloseButton"; +import Markdown from "react-native-simple-markdown"; + +export default class Details extends React.Component { + state = { + scrollY: new Animated.Value(0) + }; + + render() { + let params = this.props.navigation.state.params || {}; + const { answers, ...rest } = params.attendee; + + const twitter = answers.find(item => item.question.id === 58); + const bio = answers.find(item => item.question.id === 56); + const job = answers.find(item => item.question.id === 59); + + const attendee = { + ...rest, + twitter: twitter ? twitter.value : null, + bio: bio ? bio.value : null, + job: job ? job.value : null + }; + + const { scrollY } = this.state; + const scale = scrollY.interpolate({ + inputRange: [-300, 0, 1], + outputRange: [2, 1, 1], + extrapolate: "clamp" + }); + const translateX = 0; + const translateY = scrollY.interpolate({ + inputRange: [-300, 0, 1], + outputRange: [-50, 1, 1], + extrapolate: "clamp" + }); + + const headerOpacity = scrollY.interpolate({ + inputRange: [0, 30, 200], + outputRange: [0, 0, 1] + }); + + return ( + + {Platform.OS === "ios" ? ( + + ) : null} + + + + + + + + + + {attendee.firstName + " " + attendee.lastName} + + + + + {attendee.bio && ( + + Bio + {attendee.bio} + + )} + {attendee.job && ( + + + Job Title + + {attendee.job} + + )} + {attendee.twitter && ( + + + Twitter + + + this._handlePressSpeakerTwitter(attendee.twitter) + } + > + + {attendee.twitter} + + + + )} + + + + + ( + 0 ? -5 : 0 + }} + > + this.props.navigation.goBack()} + tintColor="#fff" + title={null} + /> + + )} + renderRightButton={() => null} + /> + + ); + } + + _renderTruncatedFooter = handlePress => { + return ( + + + Read more + + + ); + }; + + _handlePressSpeakerTwitter = async twitter => { + try { + await Linking.openURL(`twitter://user?screen_name=` + twitter); + } catch (e) { + WebBrowser.openBrowserAsync("https://twitter.com/" + twitter); + } + }; +} +const markdownStyles = { + text: {} +}; +const styles = StyleSheet.create({ + container: {}, + avatar: { + width: 100, + height: 100, + borderRadius: 50, + marginBottom: 10 + }, + content: { + backgroundColor: "#fff", + paddingBottom: 20, + paddingHorizontal: 20 + }, + headerContainer: { + backgroundColor: Colors.blue, + paddingTop: Constants.statusBarHeight + Layout.notchHeight + 20, + paddingBottom: 20, + paddingHorizontal: 20, + alignItems: "center", + justifyContent: "center" + }, + headerText: { + color: "#fff", + fontSize: FontSizes.subtitle + }, + twitterText: { + color: "#000", + fontSize: FontSizes.subtitle + }, + sectionHeader: { + fontSize: FontSizes.bodyTitle, + marginTop: 15, + marginBottom: 3 + } +}); diff --git a/src/screens/Attendees.js b/src/screens/Attendees.js new file mode 100644 index 0000000..c9c13c8 --- /dev/null +++ b/src/screens/Attendees.js @@ -0,0 +1,135 @@ +import React from "react"; + +import { + Animated, + Linking, + Platform, + Text, + Image, + TouchableOpacity, + StyleSheet, + AsyncStorage, + View +} from "react-native"; +import FadeIn from "react-native-fade-in-image"; +import { ScrollView } from "react-native-gesture-handler"; +import { WebBrowser } from "expo"; + +import { Colors, FontSizes, Layout } from "../constants"; +import MenuButton from "../components/MenuButton"; +import { BoldText, SemiBoldText, RegularText } from "../components/StyledText"; +import LoadingPlaceholder from "../components/LoadingPlaceholder"; +import CachedImage from "../components/CachedImage"; +import SearchAttendees from "../components/SearchAttendees"; +import { RectButton } from "react-native-gesture-handler"; + +export default class Attendees extends React.Component { + static navigationOptions = { + title: "Attendees", + headerStyle: { backgroundColor: Colors.blue }, + headerTintColor: "white", + headerLeft: , + headerTitleStyle: { + fontFamily: "open-sans-bold" + } + }; + + state = { + ready: Platform.OS === "android" ? false : true, + uuid: null + }; + + async getUUID() { + try { + const value = await AsyncStorage.getItem("@MySuperStore:tickets"); + const tickets = JSON.parse(value); + const tix = tickets || []; + const uuid = tix.length > 0 && tickets[0].uuid; + + this.setState({ uuid }); + } catch (err) { + console.log(err); + return []; + } + } + + constructor(props) { + super(props); + } + + componentDidMount() { + this.getUUID(); + if (this.state.ready) { + return; + } + + setTimeout(() => { + this.setState({ ready: true }); + }, 200); + } + + render() { + if (!this.state.ready) { + return null; + } + + const { uuid } = this.state; + + if (uuid) { + return ( + { + this._handlePressAttendee(attendee); + }} + /> + ); + } else { + return ( + + + + You need to scan your ticket first + + + + ); + } + } + + _handlePressProfileQRButton = () => { + this.props.navigation.navigate({ + routeName: "QRScanner", + key: "QRScanner" + }); + }; + + _handlePressAttendee = attendee => { + this.props.navigation.navigate("AttendeeDetails", { attendee }); + }; +} + +const BORDER_RADIUS = 3; + +const styles = StyleSheet.create({ + bigButton: { + backgroundColor: Colors.blue, + paddingHorizontal: 15, + height: 50, + marginHorizontal: 15, + alignItems: "center", + justifyContent: "center", + borderRadius: BORDER_RADIUS, + overflow: "hidden", + flexDirection: "row" + }, + bigButtonText: { + fontSize: FontSizes.normalButton, + color: "#fff", + textAlign: "center" + } +}); diff --git a/src/screens/Contacts.js b/src/screens/Contacts.js index 1b83ab4..e8ae19b 100644 --- a/src/screens/Contacts.js +++ b/src/screens/Contacts.js @@ -1,29 +1,29 @@ -import React from 'react'; +import React from "react"; import { Animated, Linking, Platform, StyleSheet, AsyncStorage, - View, -} from 'react-native'; -import { WebBrowser } from 'expo'; -import { RectButton } from 'react-native-gesture-handler'; -import { View as AnimatableView } from 'react-native-animatable'; -import { withNavigation } from 'react-navigation'; - -import AnimatedScrollView from '../components/AnimatedScrollView'; -import MyContacts from '../components/MyContacts'; -import NavigationBar from '../components/NavigationBar'; -import MenuButton from '../components/MenuButton'; -import { SemiBoldText } from '../components/StyledText'; -import { Colors, FontSizes, Layout } from '../constants'; -export const Schedule = require('../data/schedule.json'); + View +} from "react-native"; +import { WebBrowser } from "expo"; +import { RectButton } from "react-native-gesture-handler"; +import { View as AnimatableView } from "react-native-animatable"; +import { withNavigation } from "react-navigation"; + +import AnimatedScrollView from "../components/AnimatedScrollView"; +import MyContacts from "../components/MyContacts"; +import NavigationBar from "../components/NavigationBar"; +import MenuButton from "../components/MenuButton"; +import { SemiBoldText } from "../components/StyledText"; +import { Colors, FontSizes, Layout } from "../constants"; +export const Schedule = require("../data/schedule.json"); const Event = Schedule.events[0]; class Contacts extends React.Component { state = { - scrollY: new Animated.Value(0), + scrollY: new Animated.Value(0) }; render() { @@ -31,7 +31,7 @@ class Contacts extends React.Component { const headerOpacity = scrollY.interpolate({ inputRange: [0, 150], outputRange: [0, 1], - extrapolate: 'clamp', + extrapolate: "clamp" }); return ( @@ -43,18 +43,19 @@ class Contacts extends React.Component { onScroll={Animated.event( [ { - nativeEvent: { contentOffset: { y: scrollY } }, - }, + nativeEvent: { contentOffset: { y: scrollY } } + } ], { useNativeDriver: true } - )}> + )} + > @@ -74,14 +75,13 @@ class Contacts extends React.Component { @withNavigation class DeferredContactsContent extends React.Component { state = { - ready: Platform.OS === 'android' ? false : true, - tickets: [], + ready: Platform.OS === "android" ? false : true, + tickets: [] }; async getTickets() { try { - const value = await AsyncStorage.getItem('@MySuperStore:tickets'); - console.log('tickets', value); + const value = await AsyncStorage.getItem("@MySuperStore:tickets"); this.setState({ tickets: JSON.parse(value) }); this.tickets = JSON.parse(value); } catch (err) { @@ -110,8 +110,11 @@ class DeferredContactsContent extends React.Component { if (!this.state.ready) { return null; } - console.log('state', this.state); - const tix = this.state.tickets || []; + + const { tickets } = this.state; + const tix = tickets || []; + const uuid = tix.length > 0 && tickets[0].uuid; + return ( + underlayColor="#fff" + > {"Scan a contact badge's QR code"} + { + this._handlePressAttendee(attendee); + }} + /> ) : ( + underlayColor="#fff" + > You need to scan your ticket first @@ -147,25 +158,29 @@ class DeferredContactsContent extends React.Component { _handlePressQRButton = () => { this.props.navigation.navigate({ - routeName: 'QRContactScanner', - key: 'QRContactScanner', + routeName: "QRContactScanner", + key: "QRContactScanner" }); }; _handlePressProfileQRButton = () => { this.props.navigation.navigate({ - routeName: 'QRScanner', - key: 'QRScanner', + routeName: "QRScanner", + key: "QRScanner" }); }; + _handlePressAttendee = attendee => { + this.props.navigation.navigate("AttendeeDetails", { attendee }); + }; + _handlePressTwitterButton = async () => { try { await Linking.openURL( `twitter://user?screen_name=` + Event.twitterHandle ); } catch (e) { - WebBrowser.openBrowserAsync('https://twitter.com/' + Event.twitterHandle); + WebBrowser.openBrowserAsync("https://twitter.com/" + Event.twitterHandle); } }; } @@ -173,12 +188,12 @@ class DeferredContactsContent extends React.Component { const OverscrollView = () => ( ); @@ -187,9 +202,10 @@ const ClipBorderRadius = ({ children, style }) => { return ( + { borderRadius: BORDER_RADIUS, overflow: "hidden", marginTop: 10 }, + style + ]} + > {children} ); @@ -199,50 +215,50 @@ const BORDER_RADIUS = 3; const styles = StyleSheet.create({ headerContent: { - alignItems: 'center', + alignItems: "center", marginTop: 5, - paddingVertical: 10, + paddingVertical: 10 }, headerVideoLayer: { - ...StyleSheet.absoluteFillObject, + ...StyleSheet.absoluteFillObject }, headerVideoOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: Colors.blue, - opacity: 0.8, + opacity: 0.8 }, headerText: { - color: '#fff', - textAlign: 'center', + color: "#fff", + textAlign: "center", fontSize: 17, - lineHeight: 17 * 1.5, + lineHeight: 17 * 1.5 }, headerSmallText: { - color: '#fff', - textAlign: 'center', + color: "#fff", + textAlign: "center", fontSize: 7, - lineHeight: 7 * 1.5, + lineHeight: 7 * 1.5 }, bigButton: { backgroundColor: Colors.blue, paddingHorizontal: 15, height: 50, marginHorizontal: 15, - alignItems: 'center', - justifyContent: 'center', + alignItems: "center", + justifyContent: "center", borderRadius: BORDER_RADIUS, - overflow: 'hidden', - flexDirection: 'row', + overflow: "hidden", + flexDirection: "row" }, bigButtonText: { fontSize: FontSizes.normalButton, - color: '#fff', - textAlign: 'center', + color: "#fff", + textAlign: "center" }, seeAllTalks: { fontSize: FontSizes.normalButton, - color: Colors.blue, - }, + color: Colors.blue + } }); export default Contacts; diff --git a/src/screens/index.js b/src/screens/index.js index bebbeac..a5dd06f 100644 --- a/src/screens/index.js +++ b/src/screens/index.js @@ -1,8 +1,10 @@ import Crew from "./Crew"; import Details from "./Details"; +import AttendeeDetails from "./AttendeeDetails"; import Home from "./Home"; import ScheduleDay from "./ScheduleDay"; import Speakers from "./Speakers"; +import Attendees from "./Attendees"; import Sponsors from "./Sponsors"; import Profile from "./Profile"; import Contacts from "./Contacts"; @@ -12,6 +14,8 @@ import CheckedInAttendeeInfo from "./CheckedInAttendeeInfo"; export default { Home, Crew, + Attendees, + AttendeeDetails, Details, ScheduleDay, Speakers, diff --git a/yarn.lock b/yarn.lock index 4baab00..4451243 100644 --- a/yarn.lock +++ b/yarn.lock @@ -511,6 +511,18 @@ lodash "^4.17.4" react-native-vector-icons "4.5.0" +"@types/async@2.0.49": + version "2.0.49" + resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.49.tgz#92e33d13f74c895cb9a7f38ba97db8431ed14bc0" + +"@types/graphql@0.12.6": + version "0.12.6" + resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.12.6.tgz#3d619198585fcabe5f4e1adfb5cf5f3388c66c13" + +"@types/zen-observable@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.3.tgz#91b728599544efbb7386d8b6633693a3c2e7ade5" + abab@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" @@ -642,6 +654,90 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +apollo-boost@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/apollo-boost/-/apollo-boost-0.1.6.tgz#922d423536e408abfff816904ad2753d50feb9bf" + dependencies: + apollo-cache-inmemory "^1.2.1" + apollo-client "^2.3.1" + apollo-link "^1.0.6" + apollo-link-error "^1.0.3" + apollo-link-http "^1.3.1" + apollo-link-state "^0.4.0" + graphql-tag "^2.4.2" + +apollo-cache-inmemory@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.2.1.tgz#1f8222270aa7983eb9d2ac30057196378ab3bb01" + dependencies: + apollo-cache "^1.1.8" + apollo-utilities "^1.0.12" + graphql-anywhere "^4.1.10" + +apollo-cache@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.1.8.tgz#b078d33dec876d52b5a81a325d3c254d4e362d3c" + dependencies: + apollo-utilities "^1.0.12" + +apollo-client@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.3.1.tgz#64b204779b7e8b21f901529527a9a9c973eb7fd4" + dependencies: + "@types/zen-observable" "^0.5.3" + apollo-cache "^1.1.8" + apollo-link "^1.0.0" + apollo-link-dedup "^1.0.0" + apollo-utilities "^1.0.12" + symbol-observable "^1.0.2" + zen-observable "^0.8.0" + optionalDependencies: + "@types/async" "2.0.49" + +apollo-link-dedup@^1.0.0: + version "1.0.9" + resolved "https://registry.yarnpkg.com/apollo-link-dedup/-/apollo-link-dedup-1.0.9.tgz#3c4e4af88ef027cbddfdb857c043fd0574051dad" + dependencies: + apollo-link "^1.2.2" + +apollo-link-error@^1.0.3: + version "1.0.9" + resolved "https://registry.yarnpkg.com/apollo-link-error/-/apollo-link-error-1.0.9.tgz#83bbe019a3bca7c602c399889b313a7e5e22713f" + dependencies: + apollo-link "^1.2.2" + +apollo-link-http-common@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.4.tgz#877603f7904dc8f70242cac61808b1f8d034b2c3" + dependencies: + apollo-link "^1.2.2" + +apollo-link-http@^1.3.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.4.tgz#b80b7b4b342c655b6a5614624b076a36be368f43" + dependencies: + apollo-link "^1.2.2" + apollo-link-http-common "^0.2.4" + +apollo-link-state@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/apollo-link-state/-/apollo-link-state-0.4.1.tgz#65e9e0e12c67936b8c4b12b8438434f393104579" + dependencies: + apollo-utilities "^1.0.8" + graphql-anywhere "^4.1.0-alpha.0" + +apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.2.tgz#54c84199b18ac1af8d63553a68ca389c05217a03" + dependencies: + "@types/graphql" "0.12.6" + apollo-utilities "^1.0.0" + zen-observable-ts "^0.8.9" + +apollo-utilities@^1.0.0, apollo-utilities@^1.0.12, apollo-utilities@^1.0.8: + version "1.0.12" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.0.12.tgz#9e2b2a34cf89f3bf0d73a664effd8c1bb5d1b7f7" + append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" @@ -2936,7 +3032,17 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -graphql@^0.13.1: +graphql-anywhere@^4.1.0-alpha.0, graphql-anywhere@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-4.1.10.tgz#247ada0c8d85b9d5d6b37180973442ac68ff15de" + dependencies: + apollo-utilities "^1.0.12" + +graphql-tag@^2.4.2, graphql-tag@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.9.2.tgz#2f60a5a981375f430bf1e6e95992427dc18af686" + +graphql@^0.13.1, graphql@^0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.13.2.tgz#4c740ae3c222823e7004096f832e7b93b2108270" dependencies: @@ -4056,6 +4162,10 @@ lodash.zipobject@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz#b399f5aba8ff62a746f6979bf20b214f964dbef8" +lodash@4.17.5: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + lodash@^3.5.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" @@ -5060,6 +5170,16 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-apollo@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-2.1.4.tgz#a51059ac56f1a7997cad636a5d36398b9c93ff12" + dependencies: + fbjs "^0.8.16" + hoist-non-react-statics "^2.5.0" + invariant "^2.2.2" + lodash "4.17.5" + prop-types "^15.6.0" + 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" @@ -6069,6 +6189,10 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +symbol-observable@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" @@ -6692,3 +6816,13 @@ yauzl@2.4.1: resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" dependencies: fd-slicer "~1.0.1" + +zen-observable-ts@^0.8.9: + version "0.8.9" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.9.tgz#d3c97af08c0afdca37ebcadf7cc3ee96bda9bab1" + dependencies: + zen-observable "^0.8.0" + +zen-observable@^0.8.0: + version "0.8.8" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.8.tgz#1ea93995bf098754a58215a1e0a7309e5749ec42"