├── Constants.js ├── _resources ├── babelRelayPlugin.js └── schema.json ├── .babelrc ├── Utils └── Relay │ ├── AppRelay.js │ ├── AsRelayContainer.js │ ├── AppRelayNetworkLayer.js │ └── AsRelayRenderer.js ├── RelayQueries └── ViewerQuery.js ├── Components ├── WithFreightSansFont.js ├── WithFreightSansBoldFont.js ├── FadeIn.android.js └── FadeIn.ios.js ├── Style ├── Colors.js └── Layout.js ├── Navigation ├── ExTabScene.js ├── ExNavigationReducer.js ├── ExTabItem.js ├── ExBadge.js ├── ExTab.js ├── ExTabNavigator.js ├── ExTabBar.js ├── ExNavigationHeader.js ├── ExNavigationStackReducer.js ├── ExNavigationCard.js └── ExNavigationAnimatedView.js ├── .gitignore ├── AppComponents ├── ExText.js ├── ExIcon.js └── Schedule.js ├── LICENSE ├── package.json ├── README.md ├── .flowconfig ├── ReactConfApp.js └── .eslintrc /Constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule Constants 3 | */ 4 | 'use strict'; 5 | 6 | export default { 7 | cdnHost: 'https://s3-us-west-1.amazonaws.com/reactconf2016/', 8 | }; 9 | -------------------------------------------------------------------------------- /_resources/babelRelayPlugin.js: -------------------------------------------------------------------------------- 1 | const getBabelRelayPlugin = require('babel-relay-plugin'); 2 | const schema = require('./schema.json'); 3 | 4 | module.exports = getBabelRelayPlugin(schema.data); 5 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "passPerPreset": true, 3 | "presets": [ 4 | { "plugins": "../_resources/babelRelayPlugin.js" }, 5 | "react-native", 6 | ], 7 | "plugins": [ 8 | "transform-export-extensions", 9 | "transform-decorators-legacy", 10 | "transform-react-constant-elements" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /Utils/Relay/AppRelay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AppRelay 3 | */ 4 | 5 | import Relay from 'react-relay'; 6 | 7 | export AppRelayNetworkLayer from 'AppRelayNetworkLayer'; 8 | export AsRelayContainer from 'AsRelayContainer'; 9 | export AsRelayRenderer from 'AsRelayRenderer'; 10 | 11 | export { Relay }; 12 | -------------------------------------------------------------------------------- /RelayQueries/ViewerQuery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ViewerQuery 3 | */ 4 | 5 | import Relay from 'react-relay'; 6 | 7 | export default { 8 | queries: { 9 | viewer: Component => Relay.QL` 10 | query ViewerQuery { 11 | viewer { 12 | ${Component.getFragment('viewer')} 13 | } 14 | }`, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /Components/WithFreightSansFont.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule WithFreightSansFont 3 | */ 4 | 'use strict'; 5 | 6 | import { createCustomFontComponent } from '@exponent/with-custom-font'; 7 | 8 | import Constants from 'Constants'; 9 | 10 | export default createCustomFontComponent({ 11 | fontFamily: 'FreightSansLFPro', 12 | fontWeight: 'normal', 13 | fontFamilyAndroid: 'FreightSansLFPro', 14 | fontStyleAndroid: 'regular', 15 | uri: `${Constants.cdnHost}FreigSanLFProBoo.otf`, 16 | }); 17 | -------------------------------------------------------------------------------- /Components/WithFreightSansBoldFont.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule WithFreightSansBoldFont 3 | */ 4 | 'use strict'; 5 | 6 | import { createCustomFontComponent } from '@exponent/with-custom-font'; 7 | 8 | import Constants from 'Constants'; 9 | 10 | export default createCustomFontComponent({ 11 | fontFamily: 'FreightSansLFPro', 12 | fontWeight: 'bold', 13 | fontFamilyAndroid: 'FreightSansLFPro', 14 | fontStyleAndroid: 'bold', 15 | uri: `${Constants.cdnHost}FreigSanLFProBol.otf`, 16 | }); 17 | -------------------------------------------------------------------------------- /Style/Colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-present 650 Industries. All rights reserved. 3 | * 4 | * @providesModule Colors 5 | * @flow 6 | */ 7 | 'use strict'; 8 | 9 | export default { 10 | transparent: 'rgba(0, 0, 0, 0)', 11 | tint: 'rgb(88, 136, 219)', 12 | 13 | backgroundWhite: '#fff', 14 | backgroundGray: '#f3f5f5', 15 | lighterSeparator: '#f5f5f5', 16 | separator: '#eaeaea', 17 | darkerSeparator: '#E5E5E5', 18 | 19 | slightlyFaded: '#3C4243', 20 | 21 | text: '#484e5c', 22 | timestampText: '#c1c3c7', 23 | link: '#1F76F7', 24 | }; 25 | -------------------------------------------------------------------------------- /Navigation/ExTabScene.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExTabScene 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | NavigationExperimental, 8 | StyleSheet, 9 | Text, 10 | View, 11 | } from 'react-native'; 12 | 13 | const { 14 | AnimatedView: NavigationAnimatedView, 15 | Card: NavigationCard, 16 | Container: NavigationContainer, 17 | Header: NavigationHeader, 18 | Reducer: NavigationReducer, 19 | } = NavigationExperimental; 20 | 21 | class ExTabScene extends React.Component { 22 | 23 | render() { 24 | return ( 25 | 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Utils/Relay/AsRelayContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AsRelayContainer 3 | */ 4 | 5 | import invariant from 'invariant'; 6 | import Relay from 'react-relay'; 7 | 8 | export default function AsRelayContainer(Component) { 9 | invariant( 10 | Component.relay, 11 | 'Relay components must specify Relay container properties in `relay` static property.', 12 | Component.displayName || Component.name 13 | ); 14 | 15 | const RelayComponent = Relay.createContainer(Component, { 16 | initialVariables: Component.relay.initialVariables, 17 | fragments: Component.relay.fragments, 18 | prepareVariables: Component.relay.prepareVariables, 19 | }); 20 | 21 | RelayComponent.__proto__ = Component; 22 | 23 | return RelayComponent; 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | -------------------------------------------------------------------------------- /Utils/Relay/AppRelayNetworkLayer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AppRelayNetworkLayer 3 | */ 4 | 5 | import Relay from 'react-relay'; 6 | 7 | export default class AppRelayNetworkLayer extends Relay.DefaultNetworkLayer { 8 | sendMutation(mutation) { 9 | console.log('Mutation: ---'); 10 | console.log(mutation.getQueryString(), mutation.getVariables(), mutation.getDebugName()); 11 | console.log('/Mutation: ---'); 12 | return super.sendMutation(...arguments); 13 | } 14 | 15 | sendQueries(queries) { 16 | console.log('Queries: ---'); 17 | for (let q of queries) { 18 | console.log(q.getQueryString(), q.getVariables(), q.getID(), q.getDebugName()); 19 | } 20 | console.log('/Queries: ---'); 21 | return super.sendQueries(...arguments); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Navigation/ExNavigationReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExNavigationReducer 3 | */ 4 | 5 | import { NavigationExperimental } from 'react-native'; 6 | const { Reducer: NavigationReducer } = NavigationExperimental; 7 | const ExNavigationStackReducer = require('ExNavigationStackReducer'); 8 | 9 | function makeSimpleStackReducer(tabName) { 10 | return ( 11 | ExNavigationStackReducer({ 12 | initialStates: [ 13 | {type: `${tabName}Page`, key: 'base', title: tabName}, 14 | ], 15 | key: tabName, 16 | matchAction: () => false, 17 | }) 18 | ); 19 | } 20 | 21 | export default NavigationReducer.TabsReducer({ 22 | tabReducers: [ 23 | makeSimpleStackReducer('Schedule'), 24 | makeSimpleStackReducer('People'), 25 | makeSimpleStackReducer('Event Info'), 26 | makeSimpleStackReducer('Me'), 27 | ], 28 | }); 29 | -------------------------------------------------------------------------------- /AppComponents/ExText.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExText 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | Platform, 8 | StyleSheet, 9 | Text, 10 | } from 'react-native'; 11 | 12 | export default class ExText extends React.Component { 13 | render() { 14 | return ( 15 | 19 | {this.props.children} 20 | 21 | ); 22 | } 23 | 24 | setNativeProps(nativeProps) { 25 | this._textRef.setNativeProps(nativeProps); 26 | } 27 | 28 | _setTextRef(component) { 29 | this._textRef = component; 30 | 31 | if (typeof this.props.textRef === 'function') { 32 | this.props.textRef(component); 33 | } 34 | } 35 | } 36 | 37 | const styles = StyleSheet.create({ 38 | base: { 39 | fontFamily: 'FreightSansLFPro', 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /Components/FadeIn.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-present 650 Industries. All rights reserved. 3 | * 4 | * @providesModule FadeIn 5 | */ 6 | 'use strict'; 7 | 8 | import React, { 9 | StyleSheet, 10 | View, 11 | } from 'react-native'; 12 | 13 | export default class FadeIn extends React.Component { 14 | 15 | render() { 16 | let image = React.Children.only(this.props.children); 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | {image} 25 | 26 | ); 27 | } 28 | } 29 | 30 | let styles = StyleSheet.create({ 31 | placeholderContainer: { 32 | position: 'absolute', 33 | top: 0, 34 | left: 0, 35 | bottom: 0, 36 | right: 0, 37 | }, 38 | placeholder: { 39 | backgroundColor: '#F1F1F1', 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /Navigation/ExTabItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExTabItem 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | Text, 8 | PropTypes, 9 | View, 10 | } from 'react-native'; 11 | 12 | export default class ExTabItem extends React.Component { 13 | static propTypes = { 14 | renderIcon: PropTypes.func.isRequired, 15 | renderSelectedIcon: PropTypes.func, 16 | badgeText: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 17 | renderBadge: PropTypes.func, 18 | title: PropTypes.string, 19 | titleStyle: Text.propTypes.style, 20 | selectedTitleStyle: Text.propTypes.style, 21 | selected: PropTypes.bool, 22 | onPress: PropTypes.func, 23 | allowFontScaling: PropTypes.bool 24 | }; 25 | 26 | static defaultProps = { 27 | renderIcon: () => , 28 | }; 29 | 30 | render() { 31 | let child = React.Children.only(this.props.children); 32 | return React.cloneElement(child, { 33 | style: [child.props.style, this.props.style], 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /AppComponents/ExIcon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExIcon 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | PropTypes, 8 | } from 'react-native'; 9 | import ResponsiveImage from '@exponent/react-native-responsive-image'; 10 | 11 | import Constants from 'Constants'; 12 | 13 | export default class ExIcon extends React.Component { 14 | 15 | static propTypes = { 16 | ...ResponsiveImage.propTypes, 17 | imageName: PropTypes.string, 18 | }; 19 | 20 | static sources(imageName, options = {}) { 21 | let baseUrl = `${Constants.cdnHost}${imageName}`; 22 | 23 | return { 24 | 2: {uri: `${baseUrl}@2x.png`}, 25 | // 3: {uri: `${baseUrl}@3x.png`}, 26 | }; 27 | } 28 | 29 | setNativeProps(nativeProps) { 30 | this._image.setNativeProps(nativeProps); 31 | } 32 | 33 | render() { 34 | return ( 35 | { this._image = image; }} 37 | sources={ExIcon.sources(this.props.imageName, this.props)} 38 | fadeDuration={0} 39 | {...this.props} 40 | /> 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Exponent JS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Style/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-present 650 Industries. All rights reserved. 3 | * 4 | * @providesModule Layout 5 | */ 6 | 'use strict'; 7 | 8 | import { 9 | Dimensions, 10 | PixelRatio, 11 | Platform, 12 | NativeModules, 13 | } from 'react-native'; 14 | 15 | const { ExponentConstants } = NativeModules; 16 | 17 | let windowDimensions = Dimensions.get('window'); 18 | let isSmallDevice = (windowDimensions.width <= 320); 19 | 20 | let Layout = { 21 | isSmallDevice, 22 | pixel: 1 / PixelRatio.get(), 23 | tabBarHeight: 49, 24 | navigationBarDisplacement: 0, 25 | marginHorizontal: isSmallDevice ? 10 : 15, 26 | statusBarHeight: 20, //ExponentConstants.statusBarHeight, 27 | window: windowDimensions, 28 | }; 29 | 30 | let platformDependentLayout = {}; 31 | 32 | if (Platform.OS === 'ios') { 33 | platformDependentLayout = { 34 | navigationBarHeight: 44, 35 | }; 36 | } else { 37 | platformDependentLayout = { 38 | navigationBarHeight: 56, 39 | navigationBarDisplacement: Layout.statusBarHeight, 40 | }; 41 | } 42 | 43 | platformDependentLayout.headerHeight = Layout.statusBarHeight + platformDependentLayout.navigationBarHeight; 44 | 45 | Object.assign(Layout, platformDependentLayout); 46 | 47 | export default Layout; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-conf-experience", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "description": "React Conf 2016 Official App", 6 | "private": true, 7 | "main": "ReactConfApp.js", 8 | "author": "Adam Miskiewicz ", 9 | "exp": { 10 | "abiVersion": "UNVERSIONED", 11 | "name": "React Conf 2016", 12 | "orientation": "portrait", 13 | "primaryColor": "#f6f6f7" 14 | }, 15 | "config": { 16 | "graphqlEndpoint": "http://14a5e704.ngrok.com" 17 | }, 18 | "scripts": { 19 | "fetch-graphql": "curl $npm_package_config_graphqlEndpoint/schema.json > _resources/schema.json" 20 | }, 21 | "dependencies": { 22 | "@exponent/react-native-responsive-image": "^0.1.0", 23 | "@exponent/with-custom-font": "^2.0.0", 24 | "immutable": "^3.7.6", 25 | "react": "^0.14.7", 26 | "react-mixin": "^3.0.3", 27 | "react-native": "github:exponentjs/react-native#2016-02-09", 28 | "react-native-clone-referenced-element": "^1.0.0", 29 | "react-relay": "github:exponentjs/relay#exp-latest", 30 | "react-timer-mixin": "^0.13.3" 31 | }, 32 | "devDependencies": { 33 | "babel-eslint": "^4.1.8", 34 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 35 | "babel-plugin-transform-export-extensions": "^6.5.0", 36 | "babel-plugin-transform-react-constant-elements": "^6.5.0", 37 | "babel-preset-react-native": "^1.4.0", 38 | "babel-relay-plugin": "^0.7.0", 39 | "eslint": "^2.1.0", 40 | "eslint-plugin-react": "^3.16.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The React Conf Experience 2 | A mobile app for React Conf 2016. 3 | 4 | In this repo we experiment with NavigationExperimental and use Relay for 5 | all data fetching and such. We hope it will serve as an interesting 6 | example for people who are interested in either. 7 | 8 | ## Project 9 | 10 | Mockups from Thinkmill here: https://www.dropbox.com/sh/7spugbj4usjkmsy/AAC-iKIG2B0n8wGJSd57ngqUa?dl=0. There are quite a few complex features to implement so this is the Trello board that lists priorities and who's working on what: https://trello.com/b/1aQe7XX3/react-conf-experience. 11 | 12 | Items noted as **Necessary** are needed for the conference app to be usable. Items under **Features** are nice to have. If you'd like to help and can commit to building a feature, add yourself as a member to its card and move the card under **In Progress** so we know who's working on what. When you're done with the feature, move the card to **Done**. 13 | 14 | ## How to run it (OS X) 15 | 16 | 1. Check out this repository somewhere on your computer. 17 | 2. Get set up with Exponent by downloading the Exponent app from the Play Store (Android 4.4+) or App Store (iOS 8+). 18 | 3. Then download XDE from here: https://github.com/exponentjs/xde/releases/latest 19 | 4. Launch XDE and click Open Project; select your copy of the repository you checked out. XDE will launch the packager. 20 | 5. Use XDE to send a development link to your phone that you can open with Exponent. You can also run the iOS simulator from XDE, install Exponent on it, and open the project in the simulator. 21 | 6. Make changes in your copy of the repository and refresh Exponent; on iOS, double-tap the Exponent button, and on Android, swipe from the top and tap the refresh button in the notification center. 22 | -------------------------------------------------------------------------------- /Navigation/ExBadge.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExBadge 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | StyleSheet, 8 | Text, 9 | } from 'react-native'; 10 | 11 | import Layout from 'Layout'; 12 | 13 | export default class ExBadge extends React.Component { 14 | static propTypes = Text.propTypes; 15 | 16 | constructor(props, context) { 17 | super(props, context); 18 | 19 | this._handleLayout = this._handleLayout.bind(this); 20 | } 21 | 22 | state = { 23 | computedSize: null, 24 | }; 25 | 26 | render() { 27 | let { computedSize } = this.state; 28 | let style = {}; 29 | if (!computedSize) { 30 | style.opacity = 0; 31 | } else { 32 | style.width = Math.max(computedSize.height, computedSize.width); 33 | } 34 | 35 | return ( 36 | 41 | {this.props.children} 42 | 43 | ); 44 | } 45 | 46 | _handleLayout(event) { 47 | let { width, height } = event.nativeEvent.layout; 48 | let { computedSize } = this.state; 49 | if (computedSize && computedSize.height === height && 50 | computedSize.width === width) { 51 | return; 52 | } 53 | 54 | this.setState({ 55 | computedSize: { width, height }, 56 | }); 57 | 58 | if (this.props.onLayout) { 59 | this.props.onLayout(event); 60 | } 61 | } 62 | } 63 | 64 | let styles = StyleSheet.create({ 65 | container: { 66 | fontSize: 12, 67 | color: '#fff', 68 | backgroundColor: 'rgb(0, 122, 255)', 69 | lineHeight: 15, 70 | textAlign: 'center', 71 | borderWidth: 1 + Layout.pixel, 72 | borderColor: '#fefefe', 73 | borderRadius: 17 / 2, 74 | overflow: 'hidden', 75 | }, 76 | }); 77 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*.web.js 5 | .*/*.android.js 6 | 7 | # Some modules have their own node_modules with overlap 8 | .*/node_modules/node-haste/.* 9 | 10 | # Ugh 11 | .*/node_modules/babel.* 12 | .*/node_modules/babylon.* 13 | .*/node_modules/invariant.* 14 | 15 | # Ignore react and fbjs where there are overlaps, but don't ignore 16 | # anything that react-native relies on 17 | .*/node_modules/fbjs/lib/fetch.js 18 | .*/node_modules/fbjs/lib/ExecutionEnvironment.js 19 | .*/node_modules/fbjs/lib/ErrorUtils.js 20 | 21 | # Flow has a built-in definition for the 'react' module which we prefer to use 22 | # over the currently-untyped source 23 | .*/node_modules/react/react.js 24 | .*/node_modules/react/lib/React.js 25 | .*/node_modules/react/lib/ReactDOM.js 26 | 27 | # Ignore commoner tests 28 | .*/node_modules/commoner/test/.* 29 | 30 | # See https://github.com/facebook/flow/issues/442 31 | .*/react-tools/node_modules/commoner/lib/reader.js 32 | 33 | # Ignore jest 34 | .*/node_modules/jest-cli/.* 35 | 36 | [include] 37 | 38 | [libs] 39 | node_modules/react-native/Libraries/react-native/react-native-interface.js 40 | 41 | [options] 42 | module.system=haste 43 | 44 | esproposal.decorators=ignore 45 | esproposal.class_static_fields=enable 46 | esproposal.class_instance_fields=enable 47 | 48 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 49 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.png$' -> 'RelativeImageStub' 50 | 51 | munge_underscores=true 52 | 53 | suppress_type=$FlowIssue 54 | suppress_type=$FlowFixMe 55 | suppress_type=$FixMe 56 | 57 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 58 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ 59 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 60 | -------------------------------------------------------------------------------- /Navigation/ExTab.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExTab 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | PropTypes, 8 | StyleSheet, 9 | Text, 10 | TouchableOpacity, 11 | View, 12 | } from 'react-native'; 13 | 14 | import Layout from 'Layout'; 15 | 16 | export default class ExTab extends React.Component { 17 | static propTypes = { 18 | title: PropTypes.string, 19 | titleStyle: Text.propTypes.style, 20 | badge: PropTypes.element, 21 | onPress: PropTypes.func, 22 | hidesTabTouch: PropTypes.bool, 23 | allowFontScaling: PropTypes.bool 24 | }; 25 | 26 | constructor(props, context) { 27 | super(props, context); 28 | 29 | this._handlePress = this._handlePress.bind(this); 30 | } 31 | 32 | render() { 33 | let { title, badge } = this.props; 34 | let icon = React.Children.only(this.props.children); 35 | 36 | if (title) { 37 | title = 38 | 42 | {title} 43 | ; 44 | } 45 | 46 | if (badge) { 47 | badge = React.cloneElement(badge, { 48 | style: [styles.badge, badge.props.style], 49 | }); 50 | } 51 | 52 | let tabStyle = [styles.container, title ? null : styles.untitledContainer]; 53 | return ( 54 | 58 | 59 | {icon} 60 | {badge} 61 | 62 | {title} 63 | 64 | ); 65 | } 66 | 67 | _handlePress(event) { 68 | if (this.props.onPress) { 69 | this.props.onPress(event); 70 | } 71 | } 72 | } 73 | 74 | let styles = StyleSheet.create({ 75 | badge: { 76 | position: 'absolute', 77 | top: -6, 78 | right: -10, 79 | }, 80 | container: { 81 | flex: 1, 82 | flexDirection: 'column', 83 | justifyContent: 'flex-end', 84 | alignItems: 'center', 85 | }, 86 | untitledContainer: { 87 | paddingBottom: 13, 88 | }, 89 | title: { 90 | color: '#929292', 91 | fontSize: 10, 92 | textAlign: 'center', 93 | alignSelf: 'stretch', 94 | marginTop: 4, 95 | marginBottom: 1 + Layout.pixel, 96 | }, 97 | }); 98 | -------------------------------------------------------------------------------- /Components/FadeIn.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-present 650 Industries. All rights reserved. 3 | * 4 | * @providesModule FadeIn 5 | */ 6 | 'use strict'; 7 | 8 | import React, { 9 | Animated, 10 | StyleSheet, 11 | View, 12 | } from 'react-native'; 13 | import TimerMixin from 'react-timer-mixin'; 14 | 15 | import reactMixin from 'react-mixin'; 16 | import cloneReferencedElement from 'react-native-clone-referenced-element'; 17 | 18 | export default class FadeIn extends React.Component { 19 | 20 | constructor(props, context) { 21 | super(props, context); 22 | 23 | this.state = { 24 | placeholderContainerOpacity: new Animated.Value(1), 25 | }; 26 | } 27 | 28 | render() { 29 | let image = cloneReferencedElement(React.Children.only(this.props.children), { 30 | ref: component => { this._image = component; }, 31 | onLoadEnd: this._onLoadEnd.bind(this), 32 | }); 33 | 34 | return ( 35 | 36 | {image} 37 | 38 | 42 | 43 | 44 | 45 | ); 46 | } 47 | 48 | _onLoadEnd() { 49 | 50 | /* NOTE(brentvatne): If we animate in immediately when the onLoadEvent event 51 | fires, there are two unwanted consequences: 52 | 1. Animation feels janky - not entirely sure why that is 53 | (handled with minimumWait) 54 | 2. Many images finish loading in the same frame for some reason, and in my 55 | opinion it looks better when the images fade in separately 56 | (handled with staggerNonce) */ 57 | 58 | const minimumWait = 100; 59 | const staggerNonce = 200 * Math.random(); 60 | 61 | this.setTimeout(() => { 62 | Animated.timing(this.state.placeholderContainerOpacity, { 63 | toValue: 0, 64 | duration: 350, 65 | }).start(); 66 | }, minimumWait + staggerNonce); 67 | } 68 | } 69 | 70 | reactMixin(FadeIn.prototype, TimerMixin); 71 | 72 | let styles = StyleSheet.create({ 73 | placeholderContainer: { 74 | position: 'absolute', 75 | top: 0, 76 | left: 0, 77 | bottom: 0, 78 | right: 0, 79 | }, 80 | placeholder: { 81 | backgroundColor: '#F1F1F1', 82 | }, 83 | }); 84 | -------------------------------------------------------------------------------- /Navigation/ExTabNavigator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExTabNavigator 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | NavigationExperimental, 8 | StyleSheet, 9 | View, 10 | } from 'react-native'; 11 | 12 | const { 13 | Container: NavigationContainer, 14 | View: NavigationView, 15 | } = NavigationExperimental; 16 | 17 | import ExNavigationAnimatedView from 'ExNavigationAnimatedView'; 18 | import ExNavigationCard from 'ExNavigationCard'; 19 | import ExNavigationHeader from 'ExNavigationHeader'; 20 | import ExTabItem from 'ExTabItem'; 21 | import ExTabBar from 'ExTabBar'; 22 | 23 | class ExTabNavigator extends React.Component { 24 | 25 | static Item = ExTabItem; 26 | 27 | render() { 28 | return ( 29 | 30 | {/* Takes the current navigationState and renders a scene for each tab. 31 | * It also hides and shows these tabs depending on which index is selected 32 | * in the navigationState. */} 33 | 38 | 39 | {/* This actually renders the tab bar view (buttons etc) */ } 40 | 46 | 47 | ); 48 | } 49 | 50 | _renderTabScene(tabState, tabIndex) { 51 | { /* Each tab scene has its own stack, and we want transitions between 52 | * scenes in this substack to be animated, so we use AnimatedView 53 | * along with NavigationHeader and NavigationCard */ } 54 | return ( 55 | { 60 | return ( 61 | state.title} 66 | /> 67 | ); 68 | }} 69 | renderScene={(child, index, position, layout) => { 70 | return ( 71 | 78 | {this.props.renderScene(child, index)} 79 | 80 | ); 81 | }} 82 | /> 83 | ); 84 | } 85 | } 86 | 87 | export default NavigationContainer.create(ExTabNavigator); 88 | 89 | const styles = StyleSheet.create({ 90 | container: { 91 | flex: 1, 92 | }, 93 | tabNavigator: { 94 | flex: 1, 95 | }, 96 | }); 97 | -------------------------------------------------------------------------------- /Navigation/ExTabBar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExTabBar 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | NavigationExperimental, 8 | Platform, 9 | StyleSheet, 10 | Text, 11 | TouchableOpacity, 12 | View, 13 | } from 'react-native'; 14 | 15 | const { 16 | Container: NavigationContainer, 17 | Reducer: NavigationReducer, 18 | } = NavigationExperimental; 19 | 20 | const { 21 | JumpToAction, 22 | } = NavigationReducer.TabsReducer; 23 | 24 | import ExBadge from 'ExBadge'; 25 | import ExTab from 'ExTab'; 26 | import Layout from 'Layout'; 27 | 28 | class ExTabBar extends React.Component { 29 | 30 | static propTypes = { 31 | ...View.propTypes, 32 | shadowStyle: View.propTypes.style, 33 | }; 34 | 35 | render() { 36 | return ( 37 | 38 | {this.props.tabs.map(this._renderTab.bind(this))} 39 | 40 | 41 | ); 42 | } 43 | 44 | _navigateToTab(index) { 45 | this.props.onNavigate(JumpToAction(index)); 46 | } 47 | 48 | _renderTab(tabState, i) { 49 | let isSelected = i === this.props.index; 50 | let focusTab = () => this._navigateToTab(i); 51 | let item = this.props.renderTabItem( 52 | tabState.key, 53 | isSelected, 54 | focusTab, 55 | ); 56 | 57 | 58 | let icon; 59 | if (isSelected) { 60 | if (item.props.renderSelectedIcon) { 61 | icon = item.props.renderSelectedIcon(); 62 | } else if (item.props.renderIcon) { 63 | let defaultIcon = item.props.renderIcon(); 64 | icon = React.cloneElement(defaultIcon, { 65 | style: [defaultIcon.props.style, styles.defaultSelectedIcon], 66 | }); 67 | } 68 | } else if (!isSelected && item.props.renderIcon) { 69 | icon = item.props.renderIcon(); 70 | } 71 | 72 | let badge; 73 | if (item.props.renderBadge) { 74 | badge = item.props.renderBadge(); 75 | } else if (item.props.badgeText) { 76 | badge = {item.props.badgeText}; 77 | } 78 | 79 | return ( 80 | 93 | {icon} 94 | 95 | ); 96 | } 97 | }; 98 | 99 | export default NavigationContainer.create(ExTabBar); 100 | 101 | const styles = StyleSheet.create({ 102 | tabBar: { 103 | height: 60, 104 | flexDirection: 'row', 105 | justifyContent: 'space-around', 106 | position: 'absolute', 107 | bottom: 0, 108 | left: 0, 109 | right: 0, 110 | }, 111 | tabButton: { 112 | flex: 1, 113 | }, 114 | tabButtonText: { 115 | paddingTop: 20, 116 | textAlign: 'center', 117 | fontSize: 17, 118 | fontWeight: '500', 119 | }, 120 | shadow: { 121 | backgroundColor: 'rgba(0, 0, 0, 0.25)', 122 | height: Layout.pixel, 123 | position: 'absolute', 124 | left: 0, 125 | right: 0, 126 | top: Platform.OS === 'android' ? 0 : -Layout.pixel, 127 | }, 128 | }); 129 | -------------------------------------------------------------------------------- /Navigation/ExNavigationHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExNavigationHeader 3 | */ 4 | 'use strict'; 5 | 6 | import React, { 7 | Animated, 8 | Image, 9 | NavigationExperimental, 10 | StyleSheet, 11 | Text, 12 | TouchableOpacity, 13 | View, 14 | } from 'react-native'; 15 | 16 | const { 17 | Container: NavigationContainer, 18 | Reducer: NavigationReducer, 19 | } = NavigationExperimental; 20 | 21 | import ExIcon from 'ExIcon'; 22 | import ExText from 'ExText'; 23 | import WithFreightSansFont from 'WithFreightSansFont'; 24 | 25 | const AnimatedExText = Animated.createAnimatedComponent(ExText); 26 | const AnimatedExIcon = Animated.createAnimatedComponent(ExIcon); 27 | 28 | class ExNavigationHeader extends React.Component { 29 | 30 | componentWillMount() { 31 | this._handleBackPress = this._handleBackPress.bind(this); 32 | } 33 | 34 | render() { 35 | var state = this.props.navigationState; 36 | return ( 37 | 42 | {state.children.map(this._renderTitle, this)} 43 | {state.children.map(this._renderBackButton, this)} 44 | 45 | ); 46 | } 47 | 48 | _renderBackButton(childState, index) { 49 | if (index === 0) { 50 | return null; 51 | } 52 | 53 | return ( 54 | 64 | 67 | 68 | 69 | 70 | ); 71 | } 72 | 73 | _renderTitle(childState, index) { 74 | return ( 75 | 87 | {this.props.getTitle(childState)} 88 | 89 | ); 90 | } 91 | 92 | _handleBackPress() { 93 | this.props.onNavigate(NavigationReducer.StackReducer.PopAction()); 94 | } 95 | } 96 | 97 | ExNavigationHeader = NavigationContainer.create(ExNavigationHeader); 98 | 99 | const styles = StyleSheet.create({ 100 | title: { 101 | textAlign: 'center', 102 | marginTop: 10, 103 | fontSize: 21, 104 | color: '#0A0A0A', 105 | position: 'absolute', 106 | backgroundColor: 'transparent', 107 | top: 12, 108 | left: 0, 109 | right: 0, 110 | }, 111 | header: { 112 | backgroundColor: '#F3F5F5', 113 | paddingTop: 20, 114 | top: 0, 115 | height: 64, 116 | right: 0, 117 | left: 0, 118 | borderBottomWidth: 0.5, 119 | borderBottomColor: '#D2D2D2', 120 | position: 'absolute', 121 | }, 122 | backButtonWrapper: { 123 | width: 29, 124 | height: 37, 125 | bottom: 10, 126 | left: 2, 127 | padding: 8, 128 | position: 'absolute', 129 | }, 130 | backButtonImage: { 131 | tintColor: '#000', 132 | width: 13, 133 | height: 21, 134 | transform: [{rotate: '180deg'}], 135 | }, 136 | }); 137 | 138 | module.exports = ExNavigationHeader; 139 | -------------------------------------------------------------------------------- /Utils/Relay/AsRelayRenderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AsRelayRenderer 3 | */ 4 | 5 | import _ from 'lodash'; 6 | 7 | import React, { 8 | Alert, 9 | PropTypes, 10 | } from 'react-native'; 11 | 12 | import RelayRenderer from 'react-relay/lib/RelayRenderer'; 13 | 14 | import AsRelayContainer from 'AsRelayContainer'; 15 | 16 | /** 17 | * Builds a Relay root container from static properties defined on the wrapped component. 18 | * The return component will accept a special prop, "routeParams", that will pass the appropriate 19 | * parameters to the Relay root. 20 | */ 21 | export default function AsRelayRenderer(Component) { 22 | 23 | if (Component.relay && !Component.relay.queries) { 24 | throw new Error('Relay component must define queries for Relay Root Container.'); 25 | } 26 | 27 | const RelayComponent = AsRelayContainer(Component); 28 | 29 | // Global failure component 30 | 31 | class FailedComponent extends React.Component { 32 | componentDidMount() { 33 | if (__DEV__) { 34 | console.error(this.props.error); 35 | } 36 | 37 | Alert.alert('Network Request Failed', null, [ 38 | { text: 'Retry', onPress: () => { 39 | this.props.retry(); 40 | }}, 41 | { text: 'Cancel' }, 42 | ]); 43 | return; 44 | } 45 | 46 | render() { 47 | return ; 48 | } 49 | } 50 | 51 | class RelayRendererWrapper extends React.Component { 52 | static propTypes = { 53 | routeParams: PropTypes.object, 54 | }; 55 | 56 | static defaultProps = { 57 | routeParams: {}, 58 | }; 59 | 60 | constructor(props, ...args) { 61 | super(props, ...args); 62 | this.state = { 63 | queryConfig: this._computeQueryConfig(props), 64 | }; 65 | } 66 | 67 | render() { 68 | const emptyFragments = _.zipObject(RelayComponent.getFragmentNames().map(name => ([name, null]))); 69 | 70 | return ( 71 | { 76 | if (error) { // error 77 | return ; 78 | } else if (props) { // fetched 79 | return ; 80 | } else { // loading 81 | return ; 82 | } 83 | }} 84 | /> 85 | ); 86 | } 87 | 88 | componentWillReceiveProps(nextProps) { 89 | if (this.props.routeParams !== nextProps.routeParams) { 90 | this.setState({ 91 | queryConfig: this._computeQueryConfig(nextProps), 92 | }); 93 | } 94 | } 95 | 96 | _computeQueryConfig(props) { 97 | let queryConfig = { 98 | name: `relay-route-${RelayComponent.displayName}`, 99 | queries: { ...RelayComponent.relay.queries }, 100 | params: {}, 101 | }; 102 | 103 | /* 104 | Allows us to define: 105 | 106 | static relay = { 107 | ... 108 | paramDefinitions: ... 109 | } 110 | 111 | on the component being wrapped and we ensure these allow 112 | our given route params. 113 | */ 114 | 115 | const { relay: { paramDefinitions } } = RelayComponent; 116 | 117 | if (paramDefinitions) { 118 | queryConfig.params = _.reduce(paramDefinitions, (final, val, key) => { 119 | if (props.routeParams[key]) { 120 | final[key] = props.routeParams[key]; 121 | } 122 | return final; 123 | }, {}); 124 | } 125 | 126 | return queryConfig; 127 | } 128 | } 129 | 130 | return RelayRendererWrapper; 131 | } 132 | -------------------------------------------------------------------------------- /ReactConfApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ReactConfApp 3 | */ 4 | 5 | import React, { 6 | AppRegistry, 7 | InteractionManager, 8 | NavigationExperimental, 9 | StyleSheet, 10 | Text, 11 | View, 12 | } from 'react-native'; 13 | 14 | const { 15 | Container: NavigationContainer, 16 | RootContainer: NavigationRootContainer, 17 | } = NavigationExperimental; 18 | 19 | import { 20 | Relay, 21 | AppRelayNetworkLayer, 22 | } from 'AppRelay'; 23 | 24 | import Colors from 'Colors'; 25 | import ExNavigationReducer from 'ExNavigationReducer'; 26 | import ExIcon from 'ExIcon'; 27 | import ExText from 'ExText'; 28 | import Layout from 'Layout'; 29 | import Schedule from 'Schedule'; 30 | import ExTabNavigator from 'ExTabNavigator'; 31 | 32 | class App extends React.Component { 33 | constructor(...args) { 34 | super(...args); 35 | process.env.ENDPOINT = 'http://14a5e704.ngrok.com'; 36 | 37 | Relay.injectTaskScheduler(InteractionManager.runAfterInteractions); 38 | 39 | // Default network layer 40 | Relay.injectNetworkLayer( 41 | new AppRelayNetworkLayer(`${process.env.ENDPOINT}/graphql`, { 42 | fetchTimeout: 10000, 43 | }) 44 | ); 45 | } 46 | 47 | render() { 48 | return ( 49 | 53 | ); 54 | } 55 | 56 | _renderApp(navigationState) { 57 | if (!navigationState) { 58 | return null; 59 | } 60 | 61 | return ( 62 | 67 | 68 | ); 69 | } 70 | 71 | _renderTabItem(key, isSelected) { 72 | return ( 73 | } 77 | renderSelectedIcon={() => } 78 | selectedTitleStyle={{color: Colors.tint}} 79 | selected={isSelected} 80 | /> 81 | ); 82 | } 83 | 84 | _renderTabScene(route, index) { 85 | if (route.type === 'SchedulePage') { 86 | return ; 87 | } else if (route.type === 'ActivityInfo') { 88 | return ( 89 | 90 | Info goes here!!!!! 91 | 92 | ); 93 | } else { 94 | return ( 95 | 96 | {route.title || route.type} 97 | 98 | ); 99 | } 100 | } 101 | } 102 | 103 | class TabIcon extends React.Component { 104 | render() { 105 | let imageName, style; 106 | let { tab, selected } = this.props; 107 | 108 | if (tab === 'Schedule') { 109 | imageName = 'ScheduleIcon'; 110 | style = { width: 25, height: 28, marginBottom: -1 } 111 | } else if (tab === 'People') { 112 | imageName = 'PeopleIcon'; 113 | style = { width: 45, height: 24, marginTop: -1 } 114 | } else if (tab === 'Event Info') { 115 | imageName = 'EventInfoIcon'; 116 | style = { width: 20, height: 28, marginBottom: -1 } 117 | } else { 118 | imageName = 'MeIcon'; 119 | style = { width: 35, height: 25 } 120 | } 121 | 122 | style.tintColor = selected ? Colors.tint : '#fff'; 123 | 124 | return ( 125 | 129 | ); 130 | } 131 | } 132 | 133 | 134 | const styles = StyleSheet.create({ 135 | headerContainer: { 136 | backgroundColor: Colors.backgroundGray, 137 | height: 84, 138 | alignItems: 'center', 139 | justifyContent: 'center', 140 | paddingTop: 28, 141 | borderBottomWidth: Layout.pixel, 142 | borderBottomColor: Colors.separator, 143 | }, 144 | tabBar: { 145 | backgroundColor: '#1B1D24', 146 | }, 147 | }); 148 | 149 | AppRegistry.registerComponent('main', () => App); 150 | console.disableYellowBox = true; 151 | -------------------------------------------------------------------------------- /Navigation/ExNavigationStackReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExNavigationStackReducer 3 | * @flow 4 | */ 5 | 'use strict'; 6 | 7 | var NavigationStateUtils = require('NavigationState'); 8 | 9 | import type { 10 | NavigationState, 11 | NavigationReducer, 12 | } from 'NavigationState'; 13 | 14 | import type { 15 | BackAction, 16 | } from 'NavigationRootContainer'; 17 | 18 | export type NavigationStackReducerAction = BackAction | { 19 | type: string, 20 | }; 21 | 22 | export type NavigationStackVelocityParams = { 23 | vx: number; 24 | vy: number; 25 | }; 26 | 27 | const ActionTypes = { 28 | GESTURE_POP: 'react-native/NavigationExperimental/stack-gesture-pop', 29 | PUSH: 'react-native/NavigationExperimental/stack-push', 30 | POP: 'react-native/NavigationExperimental/stack-pop', 31 | JUMP_TO: 'react-native/NavigationExperimental/stack-jumpTo', 32 | JUMP_TO_INDEX: 'react-native/NavigationExperimental/stack-jumpToIndex', 33 | RESET: 'react-native/NavigationExperimental/stack-reset', 34 | }; 35 | 36 | const DEFAULT_KEY = 'NAV_STACK_DEFAULT_KEY'; 37 | 38 | function NavigationStackPushAction(state: NavigationState): NavigationStackReducerAction { 39 | return { 40 | type: ActionTypes.PUSH, 41 | state, 42 | }; 43 | } 44 | 45 | function NavigationStackGesturePopAction(velocity: NavigationStackVelocityParams): NavigationStackReducerAction { 46 | return { 47 | type: ActionTypes.GESTURE_POP, 48 | velocity, 49 | }; 50 | } 51 | 52 | function NavigationStackPopAction(): NavigationStackReducerAction { 53 | return { 54 | type: ActionTypes.POP, 55 | }; 56 | } 57 | 58 | function NavigationStackJumpToAction(key: string): NavigationStackReducerAction { 59 | return { 60 | type: ActionTypes.JUMP_TO, 61 | key, 62 | }; 63 | } 64 | 65 | function NavigationStackJumpToIndexAction(index: number): NavigationStackReducerAction { 66 | return { 67 | type: ActionTypes.JUMP_TO_INDEX, 68 | index, 69 | }; 70 | } 71 | 72 | function NavigationStackResetAction(children: Array, index: number): NavigationStackReducerAction { 73 | return { 74 | type: ActionTypes.RESET, 75 | index, 76 | children, 77 | }; 78 | } 79 | 80 | type StackReducerConfig = { 81 | initialStates: Array; 82 | initialIndex: ?number; 83 | key: ?string; 84 | matchAction: (action: any) => boolean; 85 | actionStateMap: (action: any) => NavigationState; 86 | }; 87 | 88 | function NavigationStackReducer({initialStates, initialIndex, key, matchAction, actionStateMap}: StackReducerConfig): NavigationReducer { 89 | return function (lastState: ?NavigationState, action: any): NavigationState { 90 | if (key == null) { 91 | key = DEFAULT_KEY; 92 | } 93 | if (initialIndex == null) { 94 | initialIndex = initialStates.length - 1; 95 | } 96 | if (!lastState) { 97 | lastState = { 98 | index: initialIndex, 99 | children: initialStates, 100 | key, 101 | }; 102 | } 103 | const lastParentState = NavigationStateUtils.getParent(lastState); 104 | if (!action || !lastParentState) { 105 | return lastState; 106 | } 107 | switch (action.type) { 108 | case ActionTypes.PUSH: 109 | return NavigationStateUtils.push( 110 | lastParentState, 111 | action.state 112 | ); 113 | case ActionTypes.POP: 114 | case ActionTypes.GESTURE_POP: 115 | case 'BackAction': 116 | if (lastParentState.index === 0 || lastParentState.children.length === 1) { 117 | return lastParentState; 118 | } 119 | return NavigationStateUtils.pop(lastParentState); 120 | case ActionTypes.JUMP_TO: 121 | return NavigationStateUtils.jumpTo( 122 | lastParentState, 123 | action.key 124 | ); 125 | case ActionTypes.JUMP_TO_INDEX: 126 | return NavigationStateUtils.jumpToIndex( 127 | lastParentState, 128 | action.index 129 | ); 130 | case ActionTypes.RESET: 131 | return { 132 | ...lastParentState, 133 | index: action.index, 134 | children: action.children, 135 | }; 136 | } 137 | if (matchAction(action)) { 138 | return NavigationStateUtils.push( 139 | lastParentState, 140 | actionStateMap(action) 141 | ); 142 | } 143 | return lastParentState; 144 | }; 145 | } 146 | 147 | NavigationStackReducer.PushAction = NavigationStackPushAction; 148 | NavigationStackReducer.PopAction = NavigationStackPopAction; 149 | NavigationStackReducer.GesturePopAction = NavigationStackGesturePopAction; 150 | NavigationStackReducer.JumpToAction = NavigationStackJumpToAction; 151 | NavigationStackReducer.JumpToIndexAction = NavigationStackJumpToIndexAction; 152 | NavigationStackReducer.ResetAction = NavigationStackResetAction; 153 | 154 | module.exports = NavigationStackReducer; 155 | -------------------------------------------------------------------------------- /Navigation/ExNavigationCard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExNavigationCard 3 | */ 4 | 'use strict'; 5 | 6 | const Animated = require('Animated'); 7 | const NavigationReducer = require('NavigationReducer'); 8 | const NavigationContainer = require('NavigationContainer'); 9 | const PanResponder = require('PanResponder'); 10 | const Platform = require('Platform'); 11 | const React = require('React'); 12 | const StyleSheet = require('StyleSheet'); 13 | const View = require('View'); 14 | 15 | // note(brentvatne): yuck 16 | const Dimensions = require('Dimensions'); 17 | const WINDOW_WIDTH = Dimensions.get('window').width; 18 | 19 | const ENABLE_GESTURES = true; 20 | const ExNavigationStackReducer = require('ExNavigationStackReducer'); 21 | 22 | import type { 23 | NavigationParentState 24 | } from 'NavigationState'; 25 | 26 | type Layout = { 27 | initWidth: number, 28 | initHeight: number, 29 | width: Animated.Value; 30 | height: Animated.Value; 31 | }; 32 | 33 | type Props = { 34 | navigationState: NavigationParentState; 35 | index: number; 36 | position: Animated.Value; 37 | layout: Layout; 38 | onNavigate: Function; 39 | children: Object; 40 | }; 41 | 42 | class NavigationCard extends React.Component { 43 | _responder: ?Object; 44 | _lastHeight: number; 45 | _lastWidth: number; 46 | _widthListener: string; 47 | _heightListener: string; 48 | props: Props; 49 | componentWillMount() { 50 | if (ENABLE_GESTURES) { 51 | this._enableGestures(); 52 | } 53 | } 54 | _enableGestures() { 55 | this._responder = PanResponder.create({ 56 | onMoveShouldSetPanResponder: (e, {dx, dy, moveX, moveY, x0, y0}) => { 57 | if (this.props.navigationState.index === 0) { 58 | return false; 59 | } 60 | if (moveX > 30) { 61 | return false; 62 | } 63 | if (dx > 5 && Math.abs(dy) < 4) { 64 | return true; 65 | } 66 | return false; 67 | }, 68 | onPanResponderGrant: (e, {dx, dy, moveX, moveY, x0, y0}) => { 69 | }, 70 | onPanResponderMove: (e, {dx}) => { 71 | const a = (-dx / this._lastWidth) + this.props.navigationState.index; 72 | this.props.position.setValue(a); 73 | }, 74 | onPanResponderRelease: (e, {vx, dx}) => { 75 | const xRatio = dx / this._lastWidth; 76 | const doesPop = (xRatio + vx) > 0.45; 77 | if (doesPop) { 78 | // todo: add an action which accepts velocity of the pop action/gesture, which is caught and used by NavigationAnimatedView 79 | this.props.onNavigate(ExNavigationStackReducer.GesturePopAction({ 80 | vx: vx, 81 | vy: 0, 82 | })); 83 | return; 84 | } 85 | Animated.spring(this.props.position, { 86 | toValue: this.props.navigationState.index, 87 | }).start(); 88 | }, 89 | onPanResponderTerminate: (e, {vx, dx}) => { 90 | Animated.spring(this.props.position, { 91 | toValue: this.props.navigationState.index, 92 | }).start(); 93 | }, 94 | }); 95 | } 96 | componentDidMount() { 97 | this._lastHeight = this.props.layout.initHeight; 98 | this._lastWidth = this.props.layout.initWidth; 99 | this._widthListener = this.props.layout.width.addListener(({value}) => { 100 | this._lastWidth = value; 101 | }); 102 | this._heightListener = this.props.layout.height.addListener(({value}) => { 103 | this._lastHeight = value; 104 | }); 105 | // todo: fix listener and last layout dimensions when props change. potential bugs here 106 | } 107 | componentWillUnmount() { 108 | this.props.layout.width.removeListener(this._widthListener); 109 | this.props.layout.height.removeListener(this._heightListener); 110 | } 111 | render() { 112 | const cardPosition = Animated.add(this.props.position, new Animated.Value(-this.props.index)); 113 | 114 | let { index } = this.props; 115 | const gestureValue = this.props.position.interpolate({ 116 | inputRange: [index - 1, index, index + 1], 117 | outputRange: [-WINDOW_WIDTH, 0, WINDOW_WIDTH / 2.5], 118 | extrapolate: 'clamp', 119 | }); 120 | 121 | const touchResponderHandlers = this._responder ? this._responder.panHandlers : null; 122 | return ( 123 | 135 | {this.props.children} 136 | 137 | 148 | 149 | ); 150 | } 151 | } 152 | 153 | NavigationCard = NavigationContainer.create(NavigationCard); 154 | 155 | const styles = StyleSheet.create({ 156 | card: { 157 | backgroundColor: '#E9E9EF', 158 | shadowColor: 'black', 159 | shadowOpacity: 0.4, 160 | shadowOffset: {width: 0, height: 0}, 161 | shadowRadius: 10, 162 | top: 0, 163 | bottom: 0, 164 | position: 'absolute', 165 | }, 166 | overlay: { 167 | backgroundColor: 'black', 168 | position: 'absolute', 169 | top: 0, 170 | left: 0, 171 | bottom: 0, 172 | right: 0, 173 | }, 174 | }); 175 | 176 | module.exports = NavigationCard; 177 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "ecmaFeatures": { 5 | "modules": true, 6 | "jsx": true 7 | }, 8 | 9 | "env": { 10 | "es6": true, 11 | "jasmine": true, 12 | "node": true 13 | }, 14 | 15 | // Map from global var to bool specifying if it can be redefined 16 | "globals": { 17 | "__DEV__": false, 18 | "Promise": false, 19 | "XMLHttpRequest": false, 20 | "fetch": false, 21 | "requestAnimationFrame": false, 22 | }, 23 | 24 | "plugins": [ 25 | "react" 26 | ], 27 | 28 | "rules": { 29 | "no-alert": 1, 30 | "no-array-constructor": 1, 31 | "no-bitwise": 0, 32 | "no-caller": 1, 33 | "no-catch-shadow": 0, 34 | "no-class-assign": 0, 35 | "no-cond-assign": 1, 36 | "no-console": 0, 37 | "no-const-assign": 2, 38 | "no-constant-condition": 1, 39 | "no-continue": 0, 40 | "no-control-regex": 1, 41 | "no-debugger": 1, 42 | "no-delete-var": 2, 43 | "no-div-regex": 1, 44 | "no-dupe-keys": 2, 45 | "no-dupe-args": 2, 46 | "no-duplicate-case": 2, 47 | "no-else-return": 0, 48 | "no-empty": 0, 49 | "no-empty-character-class": 1, 50 | "no-eq-null": 0, 51 | "no-eval": 1, 52 | "no-ex-assign": 1, 53 | "no-extend-native": 1, 54 | "no-extra-bind": 1, 55 | "no-extra-boolean-cast": 1, 56 | "no-extra-parens": 0, 57 | "no-extra-semi": 1, 58 | "no-fallthrough": 1, 59 | "no-floating-decimal": 1, 60 | "no-func-assign": 2, 61 | "no-implied-eval": 1, 62 | "no-inline-comments": 0, 63 | "no-inner-declarations": [0, "functions"], 64 | "no-invalid-regexp": 2, 65 | "no-irregular-whitespace": 1, 66 | "no-iterator": 1, 67 | "no-label-var": 1, 68 | "no-labels": 1, 69 | "no-lone-blocks": 1, 70 | "no-lonely-if": 0, 71 | "no-loop-func": 0, 72 | "no-mixed-requires": [0, false], 73 | "no-mixed-spaces-and-tabs": [1, false], 74 | "no-multi-spaces": 0, 75 | "no-multi-str": 0, 76 | "no-multiple-empty-lines": [0, {"max": 2}], 77 | "no-native-reassign": 0, 78 | "no-negated-in-lhs": 1, 79 | "no-nested-ternary": 0, 80 | "no-new": 1, 81 | "no-new-func": 1, 82 | "no-new-object": 1, 83 | "no-new-require": 1, 84 | "no-new-wrappers": 1, 85 | "no-obj-calls": 1, 86 | "no-octal": 1, 87 | "no-octal-escape": 1, 88 | "no-param-reassign": 0, 89 | "no-path-concat": 1, 90 | "no-plusplus": 0, 91 | "no-process-env": 0, 92 | "no-process-exit": 0, 93 | "no-proto": 1, 94 | "no-redeclare": 1, 95 | "no-regex-spaces": 0, 96 | "no-reserved-keys": 0, 97 | "no-restricted-modules": 0, 98 | "no-return-assign": 1, 99 | "no-script-url": 1, 100 | "no-self-compare": 1, 101 | "no-sequences": 1, 102 | "no-shadow": 0, 103 | "no-shadow-restricted-names": 1, 104 | "no-spaced-func": 1, 105 | "no-sparse-arrays": 1, 106 | "no-sync": 0, 107 | "no-ternary": 0, 108 | "no-trailing-spaces": 1, 109 | "no-this-before-super": 0, 110 | "no-throw-literal": 1, 111 | "no-undef": 2, 112 | "no-undef-init": 0, 113 | "no-undefined": 0, 114 | "no-unexpected-multiline": 1, 115 | "no-underscore-dangle": 0, 116 | "no-unneeded-ternary": 1, 117 | "no-unreachable": 1, 118 | "no-unused-expressions": 1, 119 | "no-unused-vars": [1, {"vars": "all", "args": "none"}], 120 | "no-use-before-define": 0, 121 | "no-useless-call": 0, 122 | "no-void": 1, 123 | "no-var": 0, 124 | "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], 125 | "no-with": 1, 126 | 127 | "array-bracket-spacing": [1, "never"], 128 | "arrow-parens": 0, 129 | "arrow-spacing": [1, { "before": true, "after": true }], 130 | "accessor-pairs": 0, 131 | "block-scoped-var": 0, 132 | "brace-style": [0, "1tbs"], 133 | "callback-return": 0, 134 | "camelcase": 0, 135 | "comma-dangle": [1, "always-multiline"], 136 | "comma-spacing": [1, {"before": false, "after": true}], 137 | "comma-style": [1, "last"], 138 | "complexity": [0, 11], 139 | "computed-property-spacing": [0, "never"], 140 | "consistent-return": 0, 141 | "consistent-this": [0, "self"], 142 | "constructor-super": 1, 143 | "curly": [1, "all"], 144 | "default-case": 0, 145 | "dot-location": [1, "property"], 146 | "dot-notation": [0, { "allowKeywords": true }], 147 | "eol-last": 1, 148 | "eqeqeq": [1, "smart"], 149 | "func-names": 0, 150 | "func-style": [0, "declaration"], 151 | "generator-star-spacing": [0, "after"], 152 | "guard-for-in": 0, 153 | "handle-callback-err": [1, "^(err|error)$"], 154 | "indent": [0, 2], 155 | "init-declarations": 0, 156 | "key-spacing": [0, { "beforeColon": false, "afterColon": true }], 157 | "linebreak-style": [0, "unix"], 158 | "lines-around-comment": 0, 159 | "max-depth": [0, 4], 160 | "max-len": [0, 80, 4], 161 | "max-nested-callbacks": [0, 2], 162 | "max-params": [0, 3], 163 | "max-statements": [0, 10], 164 | "new-cap": 0, 165 | "new-parens": 1, 166 | "newline-after-var": 0, 167 | "object-curly-spacing": [0, "always", { "objectsInObjects": false }], 168 | "object-shorthand": [1, "always"], 169 | "one-var": [0, "never"], 170 | "operator-assignment": [0, "always"], 171 | "operator-linebreak": [1, "after"], 172 | "padded-blocks": 0, 173 | "prefer-const": 0, 174 | "prefer-spread": 1, 175 | "quote-props": 0, 176 | "quotes": [1, "single", "avoid-escape"], 177 | "radix": 1, 178 | "require-yield": 0, 179 | "semi": 1, 180 | "semi-spacing": [1, { "before": false, "after": true }], 181 | "sort-vars": 0, 182 | "keyword-spacing": 1, 183 | "space-before-blocks": [1, "always"], 184 | "space-before-function-paren": [1, { "anonymous": "never", "named": "never" }], 185 | "space-in-parens": [1, "never"], 186 | "space-infix-ops": 1, 187 | "space-unary-ops": [0, { "words": true, "nonwords": false }], 188 | "spaced-comment": 0, 189 | "strict": [1, "global"], 190 | "use-isnan": 2, 191 | "valid-jsdoc": 0, 192 | "valid-typeof": 2, 193 | "vars-on-top": 0, 194 | "wrap-iife": 0, 195 | "wrap-regex": 0, 196 | "yoda": [1, "never", { "exceptRange": false }], 197 | 198 | // React 199 | "react/display-name": [1, { "acceptTranspilerName": true }], 200 | "react/jsx-boolean-value": [1, "never"], 201 | "react/jsx-no-undef": 2, 202 | "react/jsx-quotes": [1, "double", "avoid-escape"], 203 | "react/jsx-sort-prop-types": 0, 204 | "react/jsx-sort-props": 0, 205 | "react/jsx-uses-react": 1, 206 | "react/jsx-uses-vars": 1, 207 | "react/no-danger": 0, 208 | "react/no-did-mount-set-state": [1, "allow-in-func"], 209 | "react/no-did-update-set-state": [1, "allow-in-func"], 210 | "react/no-multi-comp": 0, 211 | "react/no-unknown-property": 1, 212 | "react/prop-types": 0, 213 | "react/react-in-jsx-scope": 2, 214 | "react/require-extension": 1, 215 | "react/self-closing-comp": 1, 216 | "react/sort-comp": 0, 217 | "react/wrap-multilines": [1, { "declaration": false, "assignment": false, "return": true}] 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /Navigation/ExNavigationAnimatedView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExNavigationAnimatedView 3 | */ 4 | 'use strict'; 5 | 6 | var Animated = require('Animated'); 7 | var Map = require('Map'); 8 | var NavigationStateUtils = require('NavigationState'); 9 | var NavigationContainer = require('NavigationContainer'); 10 | var React = require('react-native'); 11 | var View = require('View'); 12 | 13 | const USE_NATIVE_ANIMATIONS = true; 14 | 15 | import type { 16 | NavigationState, 17 | NavigationParentState, 18 | } from 'NavigationState'; 19 | 20 | type NavigationScene = { 21 | index: number, 22 | state: NavigationState, 23 | isStale: boolean, 24 | }; 25 | 26 | /** 27 | * Helper function to compare route keys (e.g. "9", "11"). 28 | */ 29 | function compareKey(one: string, two: string): number { 30 | var delta = one.length - two.length; 31 | if (delta > 0) { 32 | return 1; 33 | } 34 | if (delta < 0) { 35 | return -1; 36 | } 37 | return one > two ? 1 : -1; 38 | } 39 | 40 | /** 41 | * Helper function to sort scenes based on their index and view key. 42 | */ 43 | function compareScenes( 44 | one: NavigationScene, 45 | two: NavigationScene 46 | ): number { 47 | if (one.index > two.index) { 48 | return 1; 49 | } 50 | if (one.index < two.index) { 51 | return -1; 52 | } 53 | 54 | return compareKey( 55 | one.state.key, 56 | two.state.key 57 | ); 58 | } 59 | 60 | type Layout = { 61 | initWidth: number, 62 | initHeight: number, 63 | width: Animated.Value; 64 | height: Animated.Value; 65 | }; 66 | 67 | type OverlayRenderer = ( 68 | position: Animated.Value, 69 | layout: Layout 70 | ) => ReactElement; 71 | 72 | type SceneRenderer = ( 73 | state: NavigationState, 74 | index: number, 75 | position: Animated.Value, 76 | layout: Layout 77 | ) => ReactElement 78 | 79 | type Props = { 80 | navigationState: NavigationParentState; 81 | renderScene: SceneRenderer; 82 | renderOverlay: ?OverlayRenderer; 83 | style: any; 84 | }; 85 | 86 | class NavigationAnimatedView extends React.Component { 87 | _animatedHeight: Animated.Value; 88 | _animatedWidth: Animated.Value; 89 | _lastHeight: number; 90 | _lastWidth: number; 91 | props: Props; 92 | constructor(props) { 93 | super(props); 94 | this._animatedHeight = new Animated.Value(0); 95 | this._animatedWidth = new Animated.Value(0); 96 | this.state = { 97 | position: new Animated.Value(this.props.navigationState.index, USE_NATIVE_ANIMATIONS), 98 | scenes: new Map(), 99 | }; 100 | } 101 | componentWillMount() { 102 | this.setState({ 103 | scenes: this._reduceScenes(this.state.scenes, this.props.navigationState), 104 | }); 105 | } 106 | componentDidMount() { 107 | this.postionListener = this.state.position.addListener(this._onProgressChange.bind(this)); 108 | } 109 | componentWillReceiveProps(nextProps) { 110 | if (nextProps.navigationState !== this.props.navigationState) { 111 | this.setState({ 112 | scenes: this._reduceScenes(this.state.scenes, nextProps.navigationState, this.props.navigationState), 113 | }); 114 | } 115 | } 116 | componentDidUpdate(lastProps) { 117 | if (lastProps.navigationState.index !== this.props.navigationState.index) { 118 | this._isTransitioning = true; 119 | 120 | Animated.spring(this.state.position, { 121 | toValue: this.props.navigationState.index, 122 | bounciness: 0, 123 | speed: 15, 124 | restDisplacementThreshold: 0.1, 125 | restSpeedThreshold: 0.1, 126 | overshootClamping: true, 127 | }).start(({finished}) => { 128 | if (finished) { 129 | this._isTransitioning = false; 130 | } 131 | }); 132 | } 133 | } 134 | 135 | componentWillUnmount() { 136 | if (this.postionListener) { 137 | this.state.position.removeListener(this.postionListener); 138 | this.postionListener = null; 139 | } 140 | } 141 | 142 | onNavigate(action) { 143 | if (action.velocity) { 144 | this._isTransitioning = true; 145 | 146 | Animated.spring(this.state.position, { 147 | toValue: this.props.navigationState.index - 1, 148 | bounciness: 0, 149 | speed: 15, 150 | velocity: -action.velocity.cx, 151 | restDisplacementThreshold: 0.05, 152 | restSpeedThreshold: 0.05, 153 | overshootClamping: true, 154 | }).start(() => { 155 | this._isTransitioning = false; 156 | this.props.onNavigate(action); 157 | }); 158 | } else { 159 | if (!this._isTransitioning) { 160 | this.props.onNavigate(action); 161 | } 162 | } 163 | 164 | } 165 | 166 | getNavigationHandler() { 167 | return this.onNavigate.bind(this); 168 | } 169 | 170 | getChildContext() { 171 | return { 172 | onNavigate: this.getNavigationHandler(), 173 | }; 174 | } 175 | 176 | _onProgressChange(data: Object): void { 177 | console.log(data); 178 | if (Math.abs(data.value - this.props.navigationState.index) > Number.EPSILON) { 179 | return; 180 | } 181 | this.state.scenes.forEach((scene, index) => { 182 | if (scene.isStale) { 183 | const scenes = this.state.scenes.slice(); 184 | scenes.splice(index, 1); 185 | this.setState({ scenes, }); 186 | } 187 | }); 188 | } 189 | _reduceScenes( 190 | scenes: Array, 191 | nextState: NavigationParentState, 192 | lastState: ?NavigationParentState 193 | ): Array { 194 | let nextScenes = nextState.children.map((child, index) => { 195 | return { 196 | index, 197 | state: child, 198 | isStale: false, 199 | }; 200 | }); 201 | 202 | if (lastState) { 203 | lastState.children.forEach((child, index) => { 204 | if (!NavigationStateUtils.get(nextState, child.key)) { 205 | nextScenes.push({ 206 | index, 207 | state: child, 208 | isStale: true, 209 | }); 210 | } 211 | }); 212 | } 213 | 214 | nextScenes = nextScenes.sort(compareScenes); 215 | 216 | return nextScenes; 217 | } 218 | render() { 219 | return ( 220 | { 222 | const {height, width} = e.nativeEvent.layout; 223 | this._animatedHeight && 224 | this._animatedHeight.setValue(height); 225 | this._animatedWidth && 226 | this._animatedWidth.setValue(width); 227 | this._lastHeight = height; 228 | this._lastWidth = width; 229 | }} 230 | style={this.props.style}> 231 | {this.state.scenes.map(this._renderScene, this)} 232 | {this._renderOverlay(this._renderOverlay, this)} 233 | 234 | ); 235 | } 236 | _getLayout() { 237 | return { 238 | height: this._animatedHeight, 239 | width: this._animatedWidth, 240 | initWidth: this._lastWidth, 241 | initHeight: this._lastHeight, 242 | }; 243 | } 244 | _renderScene(scene: NavigationScene) { 245 | return this.props.renderScene( 246 | scene.state, 247 | scene.index, 248 | this.state.position, 249 | this._getLayout() 250 | ); 251 | } 252 | _renderOverlay() { 253 | const {renderOverlay} = this.props; 254 | if (renderOverlay) { 255 | return renderOverlay( 256 | this.state.position, 257 | this._getLayout() 258 | ); 259 | } 260 | return null; 261 | } 262 | } 263 | 264 | NavigationAnimatedView.contextTypes = { 265 | onNavigate: React.PropTypes.func, 266 | }; 267 | 268 | NavigationAnimatedView.childContextTypes = { 269 | onNavigate: React.PropTypes.func, 270 | }; 271 | 272 | NavigationAnimatedView = NavigationContainer.create(NavigationAnimatedView); 273 | 274 | module.exports = NavigationAnimatedView; 275 | -------------------------------------------------------------------------------- /AppComponents/Schedule.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule Schedule 3 | */ 4 | 5 | import React, { 6 | Image, 7 | ListView, 8 | NavigationExperimental, 9 | ScrollView, 10 | StyleSheet, 11 | Text, 12 | TouchableHighlight, 13 | View, 14 | } from 'react-native'; 15 | import Immutable from 'immutable'; 16 | 17 | const { 18 | Container: NavigationContainer, 19 | Reducer: NavigationReducer, 20 | } = NavigationExperimental; 21 | 22 | const { 23 | PushAction, 24 | } = NavigationReducer.StackReducer; 25 | 26 | import { 27 | AsRelayContainer, 28 | AsRelayRenderer, 29 | Relay, 30 | } from 'AppRelay'; 31 | import Colors from 'Colors'; 32 | import ExIcon from 'ExIcon'; 33 | import ExText from 'ExText'; 34 | import Layout from 'Layout'; 35 | import ViewerQuery from 'ViewerQuery'; 36 | import WithFreightSansFont from 'WithFreightSansFont'; 37 | 38 | const Events = Immutable.fromJS([ 39 | { 40 | time: new Date('Mon Feb 22 2016 09:30:00 GMT-0800 (PST)'), 41 | title: 'How To Use React In A Wedding Gift Without Being A Bad Friend', 42 | speaker: 'Keith Poplawski', 43 | speakerPhotoUri: 'http://conf.reactjs.com/img/keith-poplawski.jpg', 44 | description: 'As a belated gift, I’ve created a physical, standalone version of Jeopardy. Featuring React as the project’s interface, an Arduino and a node app running on a Raspberry Pi create an engaging and unique user experience. The presentation highlights React’s potential to respond to input beyond the mouse, including touch, physical buttons, and speech recognition.', 45 | }, 46 | { 47 | time: new Date('Mon Feb 22 2016 10:00:00 GMT-0800 (PST)'), 48 | title: 'Team × Technology', 49 | speaker: 'James Ide', 50 | speakerPhotoUri: 'http://conf.reactjs.com/img/james-ide.jpg', 51 | description: `With React Native, mobile developers are able to increase both their productivity and scope of work. The cross-platform technology is fantastic for teams building for Android and iOS, and developers can take ownership of products & features instead of single-platform implementations. At Exponent we've extended this idea to include both products and infrastructure. I'll talk a bit about how we apply this to our software development and the benefits and challenges of growing full-stack developers into cross-stack mobile developers who are responsible for Android and iOS.`, 52 | }, 53 | { 54 | time: new Date('Tue Feb 23 2016 09:30:00 GMT-0800 (PST)'), 55 | title: 'Redux, Re-frame, Relay, Om/next, oh my!', 56 | speaker: 'Jared Forsyth', 57 | speakerPhotoUri: 'http://conf.reactjs.com/img/jared-forsyth.jpg', 58 | description: 'Managing client-side state is pretty easy for TodoMVC, but soon after you move beyond that, your app can quickly get brittle, discouraging re-use and significantly complicating maintenance. I will give an overview of a few of the libraries/frameworks that have appeared recently that try to address this problem, show how each of them looks when used in the React context, and then discuss advantages, disadvantages, common patterns, and what we can learn.', 59 | }, 60 | { 61 | time: new Date('Mon Feb 22 2016 09:00:00 GMT-0800 (PST)'), 62 | title: 'Breakfast', 63 | }, 64 | { 65 | time: new Date('Tue Feb 23 2016 09:00:00 GMT-0800 (PST)'), 66 | title: 'Breakfast', 67 | }, 68 | ]); 69 | 70 | function dayOfWeekAsString(dayNumber) { 71 | return ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][dayNumber - 1]; 72 | } 73 | 74 | class Schedule extends React.Component { 75 | 76 | static relay = { 77 | queries: { ...ViewerQuery.queries }, 78 | fragments: { 79 | viewer: () => Relay.QL` 80 | fragment on Viewer { 81 | id 82 | days: schedule { 83 | date 84 | slots(first: 1000) { 85 | edges { 86 | node { 87 | ${SlotPreview.getFragment('slot')} 88 | } 89 | } 90 | } 91 | } 92 | } 93 | `, 94 | }, 95 | }; 96 | 97 | constructor(props) { 98 | super(props); 99 | 100 | let dataSource = new ListView.DataSource({ 101 | rowHasChanged: (r1, r2) => r1 !== r2, 102 | sectionHeaderHasChanged: (s1, s2) => s1 !== s2, 103 | }); 104 | 105 | this.state = { 106 | dataSource, 107 | }; 108 | } 109 | 110 | _computeSlotsByDay() { 111 | const { days } = this.props.viewer; 112 | 113 | return days.reduce((result, day) => { 114 | let dayOfWeek = dayOfWeekAsString(new Date(day.date).getDay()); 115 | result[dayOfWeek] = result[dayOfWeek] || []; 116 | result[dayOfWeek].push(...day.slots.edges.map(e => e.node)); 117 | return result; 118 | }, {}); 119 | } 120 | 121 | render() { 122 | if (this.props.relayLoading || !this.props.viewer) { 123 | return ; 124 | } 125 | 126 | let slotsByDay = this._computeSlotsByDay(); 127 | 128 | let dataSource = this.state.dataSource 129 | .cloneWithRowsAndSections(slotsByDay, Object.keys(slotsByDay)); 130 | 131 | return ( 132 | 139 | ); 140 | } 141 | 142 | _renderRow(slot) { 143 | return ( 144 | 145 | ); 146 | } 147 | 148 | _renderSeparator(sectionId, rowId) { 149 | return ( 150 | 151 | ); 152 | } 153 | 154 | _renderSectionHeader(sectionData, day) { 155 | return ( 156 | 157 | 158 | 159 | {day.toUpperCase()} 160 | 161 | 162 | 163 | ); 164 | } 165 | 166 | getNavigationHandler() { 167 | return this.props.onNavigate || this.context.onNavigate; 168 | } 169 | 170 | getChildContext() { 171 | return { 172 | onNavigate: this.getNavigationHandler(), 173 | }; 174 | } 175 | } 176 | 177 | Schedule.contextTypes = { 178 | onNavigate: React.PropTypes.func, 179 | }; 180 | 181 | Schedule.childContextTypes = { 182 | onNavigate: React.PropTypes.func, 183 | }; 184 | 185 | export default AsRelayRenderer(Schedule); 186 | 187 | const startTimeEndTimeFragment = Relay.QL` 188 | fragment on ScheduleSlotInterface { 189 | startTime 190 | endTime 191 | } 192 | `; 193 | 194 | class SlotPreview extends React.Component { 195 | 196 | static relay = { 197 | fragments: { 198 | slot: () => Relay.QL` 199 | fragment on ScheduleSlot { 200 | __typename 201 | ${startTimeEndTimeFragment} 202 | ...on ActivitySlot { 203 | title 204 | } 205 | ...on TalkSlot { 206 | talk { 207 | title 208 | speaker { 209 | name 210 | avatarUrl 211 | } 212 | } 213 | } 214 | ...on LightningTalksSlot { 215 | id 216 | talks { 217 | title 218 | speaker { 219 | name 220 | avatarUrl 221 | } 222 | } 223 | } 224 | } 225 | `, 226 | }, 227 | }; 228 | 229 | render() { 230 | let { slot } = this.props; 231 | let isSpecial = slot.__typename !== 'TalkSlot'; 232 | 233 | if (slot.__typename === 'TalkSlot') { 234 | return this._renderFullTalk(); 235 | } else if (slot.__typename === 'LightningTalksSlot') { 236 | return this._renderLightningTalk(); 237 | } else { 238 | return this._renderSpecial(); 239 | } 240 | } 241 | 242 | _renderFullTalk() { 243 | let { slot } = this.props; 244 | 245 | return ( 246 | { 249 | this.props.onNavigate(PushAction({ 250 | type: 'ActivityInfo', slot, title: 'Activity Info!' 251 | })) 252 | }}> 253 | 254 | 255 | 258 | 259 | 260 | 261 | 262 | {get12HourTime(new Date(slot.startTime))} 263 | 264 | 265 | 266 | {slot.talk.title} 267 | 268 | 269 | 270 | {slot.talk.speaker.name} 271 | 272 | 273 | 274 | 275 | 278 | 279 | 280 | 281 | ); 282 | } 283 | 284 | _renderLightningTalk() { 285 | let { slot } = this.props; 286 | 287 | return ( 288 | { 291 | this.props.onNavigate(PushAction({ 292 | type: 'ActivityInfo', slot, title: 'Activity Info!' 293 | })) 294 | }}> 295 | 296 | 297 | 298 | 301 | 302 | 303 | 304 | 305 | {get12HourTime(new Date(slot.startTime))} 306 | 307 | 308 | 309 | Lightning talks 310 | 311 | 312 | 313 | 314 | 317 | 318 | 319 | 320 | {slot.talks.map(talk => { 321 | return ( 322 | 323 | 324 | 327 | 328 | 329 | 330 | 331 | {talk.title} by {talk.speaker.name} 332 | 333 | 334 | 335 | ); 336 | })} 337 | 338 | 339 | ); 340 | } 341 | 342 | _renderSpecial() { 343 | let { slot } = this.props; 344 | 345 | return ( 346 | 347 | 348 | 349 | {get12HourTime(new Date(slot.startTime))} - {slot.title} 350 | 351 | 352 | 353 | ); 354 | } 355 | } 356 | 357 | SlotPreview = AsRelayContainer(SlotPreview); 358 | 359 | function get12HourTime(date) { 360 | let hours = date.getHours() > 12 ? date.getHours() - 12 : date.getHours(); 361 | let amPm = date.getHours() >= 12 ? "PM" : "AM"; 362 | let minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(); 363 | return hours + ":" + minutes + amPm; 364 | } 365 | 366 | const styles = StyleSheet.create({ 367 | sectionHeaderContainer: { 368 | backgroundColor: Colors.backgroundGray, 369 | borderBottomWidth: Layout.pixel, 370 | borderBottomColor: Colors.separator, 371 | paddingTop: 15, 372 | paddingBottom: 5, 373 | paddingLeft: 12, 374 | }, 375 | sectionHeaderText: { 376 | fontSize: 14, 377 | }, 378 | slotPreviewMultipleImagesColumn: { 379 | flexDirection: 'row', 380 | flex: 1, 381 | paddingBottom: 10, 382 | paddingRight: 30, 383 | }, 384 | speakerPhoto: { 385 | width: 40, 386 | height: 40, 387 | borderRadius: 20, 388 | marginBottom: 3, 389 | }, 390 | titleDetailsText: { 391 | color: Colors.slightlyFaded, 392 | }, 393 | titleText: { 394 | fontSize: 18, 395 | }, 396 | subtitleText: { 397 | fontSize: 16, 398 | color: '#888', 399 | paddingTop: 5, 400 | }, 401 | rowSeparator: { 402 | height: Layout.pixel, 403 | backgroundColor: Colors.separator, 404 | flex: 1, 405 | }, 406 | slotPreviewContainer: { 407 | flexDirection: 'row', 408 | backgroundColor: Colors.backgroundWhite, 409 | padding: 15, 410 | }, 411 | slotPreviewLightningContainer: { 412 | flexDirection: 'column', 413 | backgroundColor: Colors.backgroundWhite, 414 | padding: 15, 415 | }, 416 | slotPreviewLightningInnerContainer: { 417 | flexDirection: 'row', 418 | }, 419 | slotPreviewDescriptionColumn: { 420 | flex: 1, 421 | paddingRight: 40, 422 | }, 423 | slotPreviewImageColumn: { 424 | width: 50, 425 | justifyContent: 'center', 426 | paddingTop: 2, 427 | }, 428 | slotPreviewMultipleImagesColumn: { 429 | flex: 1, 430 | }, 431 | slotPreviewCaratColumn: { 432 | width: 20, 433 | paddingTop: 15, 434 | justifyContent: 'center', 435 | }, 436 | slotPreviewCarat: { 437 | alignSelf: 'center', 438 | width: 8, 439 | height: 13, 440 | }, 441 | }); 442 | -------------------------------------------------------------------------------- /_resources/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "__schema": { 4 | "queryType": { 5 | "name": "RootQuery" 6 | }, 7 | "mutationType": null, 8 | "subscriptionType": null, 9 | "types": [ 10 | { 11 | "kind": "OBJECT", 12 | "name": "RootQuery", 13 | "description": null, 14 | "fields": [ 15 | { 16 | "name": "viewer", 17 | "description": null, 18 | "args": [], 19 | "type": { 20 | "kind": "OBJECT", 21 | "name": "Viewer", 22 | "ofType": null 23 | }, 24 | "isDeprecated": false, 25 | "deprecationReason": null 26 | }, 27 | { 28 | "name": "node", 29 | "description": "Fetches an object given its ID", 30 | "args": [ 31 | { 32 | "name": "id", 33 | "description": "The ID of an object", 34 | "type": { 35 | "kind": "NON_NULL", 36 | "name": null, 37 | "ofType": { 38 | "kind": "SCALAR", 39 | "name": "ID", 40 | "ofType": null 41 | } 42 | }, 43 | "defaultValue": null 44 | } 45 | ], 46 | "type": { 47 | "kind": "INTERFACE", 48 | "name": "Node", 49 | "ofType": null 50 | }, 51 | "isDeprecated": false, 52 | "deprecationReason": null 53 | } 54 | ], 55 | "inputFields": null, 56 | "interfaces": [], 57 | "enumValues": null, 58 | "possibleTypes": null 59 | }, 60 | { 61 | "kind": "OBJECT", 62 | "name": "Viewer", 63 | "description": "Viewer of the application", 64 | "fields": [ 65 | { 66 | "name": "id", 67 | "description": "The ID of an object", 68 | "args": [], 69 | "type": { 70 | "kind": "NON_NULL", 71 | "name": null, 72 | "ofType": { 73 | "kind": "SCALAR", 74 | "name": "ID", 75 | "ofType": null 76 | } 77 | }, 78 | "isDeprecated": false, 79 | "deprecationReason": null 80 | }, 81 | { 82 | "name": "me", 83 | "description": "The current logged-in user.", 84 | "args": [], 85 | "type": { 86 | "kind": "OBJECT", 87 | "name": "User", 88 | "ofType": null 89 | }, 90 | "isDeprecated": false, 91 | "deprecationReason": null 92 | }, 93 | { 94 | "name": "schedule", 95 | "description": "Schedule of the current event", 96 | "args": [], 97 | "type": { 98 | "kind": "LIST", 99 | "name": null, 100 | "ofType": { 101 | "kind": "OBJECT", 102 | "name": "Schedule", 103 | "ofType": null 104 | } 105 | }, 106 | "isDeprecated": false, 107 | "deprecationReason": null 108 | }, 109 | { 110 | "name": "allSpeakers", 111 | "description": null, 112 | "args": [ 113 | { 114 | "name": "after", 115 | "description": null, 116 | "type": { 117 | "kind": "SCALAR", 118 | "name": "String", 119 | "ofType": null 120 | }, 121 | "defaultValue": null 122 | }, 123 | { 124 | "name": "first", 125 | "description": null, 126 | "type": { 127 | "kind": "SCALAR", 128 | "name": "Int", 129 | "ofType": null 130 | }, 131 | "defaultValue": null 132 | }, 133 | { 134 | "name": "before", 135 | "description": null, 136 | "type": { 137 | "kind": "SCALAR", 138 | "name": "String", 139 | "ofType": null 140 | }, 141 | "defaultValue": null 142 | }, 143 | { 144 | "name": "last", 145 | "description": null, 146 | "type": { 147 | "kind": "SCALAR", 148 | "name": "Int", 149 | "ofType": null 150 | }, 151 | "defaultValue": null 152 | } 153 | ], 154 | "type": { 155 | "kind": "OBJECT", 156 | "name": "SpeakerConnection", 157 | "ofType": null 158 | }, 159 | "isDeprecated": false, 160 | "deprecationReason": null 161 | }, 162 | { 163 | "name": "speakersByName", 164 | "description": "A connection providing a list of conference speakers that can be filtered by name.", 165 | "args": [], 166 | "type": { 167 | "kind": "LIST", 168 | "name": null, 169 | "ofType": { 170 | "kind": "OBJECT", 171 | "name": "Speaker", 172 | "ofType": null 173 | } 174 | }, 175 | "isDeprecated": false, 176 | "deprecationReason": null 177 | }, 178 | { 179 | "name": "eventInfo", 180 | "description": "Information about the current event.", 181 | "args": [], 182 | "type": { 183 | "kind": "OBJECT", 184 | "name": "Event", 185 | "ofType": null 186 | }, 187 | "isDeprecated": false, 188 | "deprecationReason": null 189 | } 190 | ], 191 | "inputFields": null, 192 | "interfaces": [ 193 | { 194 | "kind": "INTERFACE", 195 | "name": "Node", 196 | "ofType": null 197 | } 198 | ], 199 | "enumValues": null, 200 | "possibleTypes": null 201 | }, 202 | { 203 | "kind": "INTERFACE", 204 | "name": "Node", 205 | "description": "An object with an ID", 206 | "fields": [ 207 | { 208 | "name": "id", 209 | "description": "The id of the object.", 210 | "args": [], 211 | "type": { 212 | "kind": "NON_NULL", 213 | "name": null, 214 | "ofType": { 215 | "kind": "SCALAR", 216 | "name": "ID", 217 | "ofType": null 218 | } 219 | }, 220 | "isDeprecated": false, 221 | "deprecationReason": null 222 | } 223 | ], 224 | "inputFields": null, 225 | "interfaces": null, 226 | "enumValues": null, 227 | "possibleTypes": [ 228 | { 229 | "kind": "OBJECT", 230 | "name": "Event", 231 | "ofType": null 232 | }, 233 | { 234 | "kind": "OBJECT", 235 | "name": "FullLengthTalk", 236 | "ofType": null 237 | }, 238 | { 239 | "kind": "OBJECT", 240 | "name": "LightningTalk", 241 | "ofType": null 242 | }, 243 | { 244 | "kind": "OBJECT", 245 | "name": "Speaker", 246 | "ofType": null 247 | }, 248 | { 249 | "kind": "OBJECT", 250 | "name": "ActivitySlot", 251 | "ofType": null 252 | }, 253 | { 254 | "kind": "OBJECT", 255 | "name": "LightningTalksSlot", 256 | "ofType": null 257 | }, 258 | { 259 | "kind": "OBJECT", 260 | "name": "TalkSlot", 261 | "ofType": null 262 | }, 263 | { 264 | "kind": "OBJECT", 265 | "name": "Schedule", 266 | "ofType": null 267 | }, 268 | { 269 | "kind": "OBJECT", 270 | "name": "User", 271 | "ofType": null 272 | }, 273 | { 274 | "kind": "OBJECT", 275 | "name": "Viewer", 276 | "ofType": null 277 | } 278 | ] 279 | }, 280 | { 281 | "kind": "OBJECT", 282 | "name": "Event", 283 | "description": "Information about an event", 284 | "fields": [ 285 | { 286 | "name": "id", 287 | "description": "The ID of an object", 288 | "args": [], 289 | "type": { 290 | "kind": "NON_NULL", 291 | "name": null, 292 | "ofType": { 293 | "kind": "SCALAR", 294 | "name": "ID", 295 | "ofType": null 296 | } 297 | }, 298 | "isDeprecated": false, 299 | "deprecationReason": null 300 | }, 301 | { 302 | "name": "name", 303 | "description": null, 304 | "args": [], 305 | "type": { 306 | "kind": "SCALAR", 307 | "name": "String", 308 | "ofType": null 309 | }, 310 | "isDeprecated": false, 311 | "deprecationReason": null 312 | }, 313 | { 314 | "name": "location", 315 | "description": null, 316 | "args": [], 317 | "type": { 318 | "kind": "OBJECT", 319 | "name": "Point", 320 | "ofType": null 321 | }, 322 | "isDeprecated": false, 323 | "deprecationReason": null 324 | }, 325 | { 326 | "name": "codeOfConduct", 327 | "description": null, 328 | "args": [], 329 | "type": { 330 | "kind": "SCALAR", 331 | "name": "String", 332 | "ofType": null 333 | }, 334 | "isDeprecated": false, 335 | "deprecationReason": null 336 | } 337 | ], 338 | "inputFields": null, 339 | "interfaces": [ 340 | { 341 | "kind": "INTERFACE", 342 | "name": "Node", 343 | "ofType": null 344 | } 345 | ], 346 | "enumValues": null, 347 | "possibleTypes": null 348 | }, 349 | { 350 | "kind": "SCALAR", 351 | "name": "ID", 352 | "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", 353 | "fields": null, 354 | "inputFields": null, 355 | "interfaces": null, 356 | "enumValues": null, 357 | "possibleTypes": null 358 | }, 359 | { 360 | "kind": "SCALAR", 361 | "name": "String", 362 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", 363 | "fields": null, 364 | "inputFields": null, 365 | "interfaces": null, 366 | "enumValues": null, 367 | "possibleTypes": null 368 | }, 369 | { 370 | "kind": "OBJECT", 371 | "name": "Point", 372 | "description": null, 373 | "fields": [ 374 | { 375 | "name": "latitude", 376 | "description": null, 377 | "args": [], 378 | "type": { 379 | "kind": "SCALAR", 380 | "name": "Float", 381 | "ofType": null 382 | }, 383 | "isDeprecated": false, 384 | "deprecationReason": null 385 | }, 386 | { 387 | "name": "longitude", 388 | "description": null, 389 | "args": [], 390 | "type": { 391 | "kind": "SCALAR", 392 | "name": "Float", 393 | "ofType": null 394 | }, 395 | "isDeprecated": false, 396 | "deprecationReason": null 397 | } 398 | ], 399 | "inputFields": null, 400 | "interfaces": [], 401 | "enumValues": null, 402 | "possibleTypes": null 403 | }, 404 | { 405 | "kind": "SCALAR", 406 | "name": "Float", 407 | "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ", 408 | "fields": null, 409 | "inputFields": null, 410 | "interfaces": null, 411 | "enumValues": null, 412 | "possibleTypes": null 413 | }, 414 | { 415 | "kind": "OBJECT", 416 | "name": "FullLengthTalk", 417 | "description": "A talk", 418 | "fields": [ 419 | { 420 | "name": "id", 421 | "description": "The ID of an object", 422 | "args": [], 423 | "type": { 424 | "kind": "NON_NULL", 425 | "name": null, 426 | "ofType": { 427 | "kind": "SCALAR", 428 | "name": "ID", 429 | "ofType": null 430 | } 431 | }, 432 | "isDeprecated": false, 433 | "deprecationReason": null 434 | }, 435 | { 436 | "name": "title", 437 | "description": null, 438 | "args": [], 439 | "type": { 440 | "kind": "SCALAR", 441 | "name": "String", 442 | "ofType": null 443 | }, 444 | "isDeprecated": false, 445 | "deprecationReason": null 446 | }, 447 | { 448 | "name": "description", 449 | "description": null, 450 | "args": [], 451 | "type": { 452 | "kind": "SCALAR", 453 | "name": "String", 454 | "ofType": null 455 | }, 456 | "isDeprecated": false, 457 | "deprecationReason": null 458 | }, 459 | { 460 | "name": "speaker", 461 | "description": null, 462 | "args": [], 463 | "type": { 464 | "kind": "OBJECT", 465 | "name": "Speaker", 466 | "ofType": null 467 | }, 468 | "isDeprecated": false, 469 | "deprecationReason": null 470 | } 471 | ], 472 | "inputFields": null, 473 | "interfaces": [ 474 | { 475 | "kind": "INTERFACE", 476 | "name": "Node", 477 | "ofType": null 478 | } 479 | ], 480 | "enumValues": null, 481 | "possibleTypes": null 482 | }, 483 | { 484 | "kind": "OBJECT", 485 | "name": "Speaker", 486 | "description": "A speaker at a conference", 487 | "fields": [ 488 | { 489 | "name": "id", 490 | "description": "The ID of an object", 491 | "args": [], 492 | "type": { 493 | "kind": "NON_NULL", 494 | "name": null, 495 | "ofType": { 496 | "kind": "SCALAR", 497 | "name": "ID", 498 | "ofType": null 499 | } 500 | }, 501 | "isDeprecated": false, 502 | "deprecationReason": null 503 | }, 504 | { 505 | "name": "name", 506 | "description": null, 507 | "args": [], 508 | "type": { 509 | "kind": "SCALAR", 510 | "name": "String", 511 | "ofType": null 512 | }, 513 | "isDeprecated": false, 514 | "deprecationReason": null 515 | }, 516 | { 517 | "name": "email", 518 | "description": null, 519 | "args": [], 520 | "type": { 521 | "kind": "SCALAR", 522 | "name": "String", 523 | "ofType": null 524 | }, 525 | "isDeprecated": false, 526 | "deprecationReason": null 527 | }, 528 | { 529 | "name": "company", 530 | "description": null, 531 | "args": [], 532 | "type": { 533 | "kind": "SCALAR", 534 | "name": "String", 535 | "ofType": null 536 | }, 537 | "isDeprecated": false, 538 | "deprecationReason": null 539 | }, 540 | { 541 | "name": "avatarUrl", 542 | "description": null, 543 | "args": [], 544 | "type": { 545 | "kind": "SCALAR", 546 | "name": "String", 547 | "ofType": null 548 | }, 549 | "isDeprecated": false, 550 | "deprecationReason": null 551 | }, 552 | { 553 | "name": "talks", 554 | "description": null, 555 | "args": [], 556 | "type": { 557 | "kind": "LIST", 558 | "name": null, 559 | "ofType": { 560 | "kind": "UNION", 561 | "name": "Talk", 562 | "ofType": null 563 | } 564 | }, 565 | "isDeprecated": false, 566 | "deprecationReason": null 567 | } 568 | ], 569 | "inputFields": null, 570 | "interfaces": [ 571 | { 572 | "kind": "INTERFACE", 573 | "name": "Node", 574 | "ofType": null 575 | } 576 | ], 577 | "enumValues": null, 578 | "possibleTypes": null 579 | }, 580 | { 581 | "kind": "UNION", 582 | "name": "Talk", 583 | "description": "A talk", 584 | "fields": null, 585 | "inputFields": null, 586 | "interfaces": null, 587 | "enumValues": null, 588 | "possibleTypes": [ 589 | { 590 | "kind": "OBJECT", 591 | "name": "FullLengthTalk", 592 | "ofType": null 593 | }, 594 | { 595 | "kind": "OBJECT", 596 | "name": "LightningTalk", 597 | "ofType": null 598 | } 599 | ] 600 | }, 601 | { 602 | "kind": "OBJECT", 603 | "name": "LightningTalk", 604 | "description": "A lightning talk", 605 | "fields": [ 606 | { 607 | "name": "id", 608 | "description": "The ID of an object", 609 | "args": [], 610 | "type": { 611 | "kind": "NON_NULL", 612 | "name": null, 613 | "ofType": { 614 | "kind": "SCALAR", 615 | "name": "ID", 616 | "ofType": null 617 | } 618 | }, 619 | "isDeprecated": false, 620 | "deprecationReason": null 621 | }, 622 | { 623 | "name": "title", 624 | "description": null, 625 | "args": [], 626 | "type": { 627 | "kind": "SCALAR", 628 | "name": "String", 629 | "ofType": null 630 | }, 631 | "isDeprecated": false, 632 | "deprecationReason": null 633 | }, 634 | { 635 | "name": "speaker", 636 | "description": null, 637 | "args": [], 638 | "type": { 639 | "kind": "OBJECT", 640 | "name": "Speaker", 641 | "ofType": null 642 | }, 643 | "isDeprecated": false, 644 | "deprecationReason": null 645 | } 646 | ], 647 | "inputFields": null, 648 | "interfaces": [ 649 | { 650 | "kind": "INTERFACE", 651 | "name": "Node", 652 | "ofType": null 653 | } 654 | ], 655 | "enumValues": null, 656 | "possibleTypes": null 657 | }, 658 | { 659 | "kind": "OBJECT", 660 | "name": "ActivitySlot", 661 | "description": "An activity (break/lunch/etc.)", 662 | "fields": [ 663 | { 664 | "name": "id", 665 | "description": "The ID of an object", 666 | "args": [], 667 | "type": { 668 | "kind": "NON_NULL", 669 | "name": null, 670 | "ofType": { 671 | "kind": "SCALAR", 672 | "name": "ID", 673 | "ofType": null 674 | } 675 | }, 676 | "isDeprecated": false, 677 | "deprecationReason": null 678 | }, 679 | { 680 | "name": "title", 681 | "description": null, 682 | "args": [], 683 | "type": { 684 | "kind": "SCALAR", 685 | "name": "String", 686 | "ofType": null 687 | }, 688 | "isDeprecated": false, 689 | "deprecationReason": null 690 | }, 691 | { 692 | "name": "startTime", 693 | "description": null, 694 | "args": [], 695 | "type": { 696 | "kind": "SCALAR", 697 | "name": "DateTime", 698 | "ofType": null 699 | }, 700 | "isDeprecated": false, 701 | "deprecationReason": null 702 | }, 703 | { 704 | "name": "endTime", 705 | "description": null, 706 | "args": [], 707 | "type": { 708 | "kind": "SCALAR", 709 | "name": "DateTime", 710 | "ofType": null 711 | }, 712 | "isDeprecated": false, 713 | "deprecationReason": null 714 | } 715 | ], 716 | "inputFields": null, 717 | "interfaces": [ 718 | { 719 | "kind": "INTERFACE", 720 | "name": "ScheduleSlotInterface", 721 | "ofType": null 722 | }, 723 | { 724 | "kind": "INTERFACE", 725 | "name": "Node", 726 | "ofType": null 727 | } 728 | ], 729 | "enumValues": null, 730 | "possibleTypes": null 731 | }, 732 | { 733 | "kind": "INTERFACE", 734 | "name": "ScheduleSlotInterface", 735 | "description": null, 736 | "fields": [ 737 | { 738 | "name": "startTime", 739 | "description": null, 740 | "args": [], 741 | "type": { 742 | "kind": "SCALAR", 743 | "name": "DateTime", 744 | "ofType": null 745 | }, 746 | "isDeprecated": false, 747 | "deprecationReason": null 748 | }, 749 | { 750 | "name": "endTime", 751 | "description": null, 752 | "args": [], 753 | "type": { 754 | "kind": "SCALAR", 755 | "name": "DateTime", 756 | "ofType": null 757 | }, 758 | "isDeprecated": false, 759 | "deprecationReason": null 760 | } 761 | ], 762 | "inputFields": null, 763 | "interfaces": null, 764 | "enumValues": null, 765 | "possibleTypes": [ 766 | { 767 | "kind": "OBJECT", 768 | "name": "ActivitySlot", 769 | "ofType": null 770 | }, 771 | { 772 | "kind": "OBJECT", 773 | "name": "LightningTalksSlot", 774 | "ofType": null 775 | }, 776 | { 777 | "kind": "OBJECT", 778 | "name": "TalkSlot", 779 | "ofType": null 780 | } 781 | ] 782 | }, 783 | { 784 | "kind": "OBJECT", 785 | "name": "LightningTalksSlot", 786 | "description": null, 787 | "fields": [ 788 | { 789 | "name": "id", 790 | "description": "The ID of an object", 791 | "args": [], 792 | "type": { 793 | "kind": "NON_NULL", 794 | "name": null, 795 | "ofType": { 796 | "kind": "SCALAR", 797 | "name": "ID", 798 | "ofType": null 799 | } 800 | }, 801 | "isDeprecated": false, 802 | "deprecationReason": null 803 | }, 804 | { 805 | "name": "talks", 806 | "description": null, 807 | "args": [], 808 | "type": { 809 | "kind": "LIST", 810 | "name": null, 811 | "ofType": { 812 | "kind": "OBJECT", 813 | "name": "LightningTalk", 814 | "ofType": null 815 | } 816 | }, 817 | "isDeprecated": false, 818 | "deprecationReason": null 819 | }, 820 | { 821 | "name": "startTime", 822 | "description": null, 823 | "args": [], 824 | "type": { 825 | "kind": "SCALAR", 826 | "name": "DateTime", 827 | "ofType": null 828 | }, 829 | "isDeprecated": false, 830 | "deprecationReason": null 831 | }, 832 | { 833 | "name": "endTime", 834 | "description": null, 835 | "args": [], 836 | "type": { 837 | "kind": "SCALAR", 838 | "name": "DateTime", 839 | "ofType": null 840 | }, 841 | "isDeprecated": false, 842 | "deprecationReason": null 843 | } 844 | ], 845 | "inputFields": null, 846 | "interfaces": [ 847 | { 848 | "kind": "INTERFACE", 849 | "name": "ScheduleSlotInterface", 850 | "ofType": null 851 | }, 852 | { 853 | "kind": "INTERFACE", 854 | "name": "Node", 855 | "ofType": null 856 | } 857 | ], 858 | "enumValues": null, 859 | "possibleTypes": null 860 | }, 861 | { 862 | "kind": "SCALAR", 863 | "name": "DateTime", 864 | "description": null, 865 | "fields": null, 866 | "inputFields": null, 867 | "interfaces": null, 868 | "enumValues": null, 869 | "possibleTypes": null 870 | }, 871 | { 872 | "kind": "OBJECT", 873 | "name": "TalkSlot", 874 | "description": null, 875 | "fields": [ 876 | { 877 | "name": "id", 878 | "description": "The ID of an object", 879 | "args": [], 880 | "type": { 881 | "kind": "NON_NULL", 882 | "name": null, 883 | "ofType": { 884 | "kind": "SCALAR", 885 | "name": "ID", 886 | "ofType": null 887 | } 888 | }, 889 | "isDeprecated": false, 890 | "deprecationReason": null 891 | }, 892 | { 893 | "name": "talk", 894 | "description": null, 895 | "args": [], 896 | "type": { 897 | "kind": "OBJECT", 898 | "name": "FullLengthTalk", 899 | "ofType": null 900 | }, 901 | "isDeprecated": false, 902 | "deprecationReason": null 903 | }, 904 | { 905 | "name": "startTime", 906 | "description": null, 907 | "args": [], 908 | "type": { 909 | "kind": "SCALAR", 910 | "name": "DateTime", 911 | "ofType": null 912 | }, 913 | "isDeprecated": false, 914 | "deprecationReason": null 915 | }, 916 | { 917 | "name": "endTime", 918 | "description": null, 919 | "args": [], 920 | "type": { 921 | "kind": "SCALAR", 922 | "name": "DateTime", 923 | "ofType": null 924 | }, 925 | "isDeprecated": false, 926 | "deprecationReason": null 927 | } 928 | ], 929 | "inputFields": null, 930 | "interfaces": [ 931 | { 932 | "kind": "INTERFACE", 933 | "name": "ScheduleSlotInterface", 934 | "ofType": null 935 | }, 936 | { 937 | "kind": "INTERFACE", 938 | "name": "Node", 939 | "ofType": null 940 | } 941 | ], 942 | "enumValues": null, 943 | "possibleTypes": null 944 | }, 945 | { 946 | "kind": "OBJECT", 947 | "name": "Schedule", 948 | "description": null, 949 | "fields": [ 950 | { 951 | "name": "id", 952 | "description": "The ID of an object", 953 | "args": [], 954 | "type": { 955 | "kind": "NON_NULL", 956 | "name": null, 957 | "ofType": { 958 | "kind": "SCALAR", 959 | "name": "ID", 960 | "ofType": null 961 | } 962 | }, 963 | "isDeprecated": false, 964 | "deprecationReason": null 965 | }, 966 | { 967 | "name": "date", 968 | "description": null, 969 | "args": [], 970 | "type": { 971 | "kind": "SCALAR", 972 | "name": "DateTime", 973 | "ofType": null 974 | }, 975 | "isDeprecated": false, 976 | "deprecationReason": null 977 | }, 978 | { 979 | "name": "slots", 980 | "description": null, 981 | "args": [ 982 | { 983 | "name": "after", 984 | "description": null, 985 | "type": { 986 | "kind": "SCALAR", 987 | "name": "String", 988 | "ofType": null 989 | }, 990 | "defaultValue": null 991 | }, 992 | { 993 | "name": "first", 994 | "description": null, 995 | "type": { 996 | "kind": "SCALAR", 997 | "name": "Int", 998 | "ofType": null 999 | }, 1000 | "defaultValue": null 1001 | }, 1002 | { 1003 | "name": "before", 1004 | "description": null, 1005 | "type": { 1006 | "kind": "SCALAR", 1007 | "name": "String", 1008 | "ofType": null 1009 | }, 1010 | "defaultValue": null 1011 | }, 1012 | { 1013 | "name": "last", 1014 | "description": null, 1015 | "type": { 1016 | "kind": "SCALAR", 1017 | "name": "Int", 1018 | "ofType": null 1019 | }, 1020 | "defaultValue": null 1021 | } 1022 | ], 1023 | "type": { 1024 | "kind": "OBJECT", 1025 | "name": "ScheduleSlotConnection", 1026 | "ofType": null 1027 | }, 1028 | "isDeprecated": false, 1029 | "deprecationReason": null 1030 | } 1031 | ], 1032 | "inputFields": null, 1033 | "interfaces": [ 1034 | { 1035 | "kind": "INTERFACE", 1036 | "name": "Node", 1037 | "ofType": null 1038 | } 1039 | ], 1040 | "enumValues": null, 1041 | "possibleTypes": null 1042 | }, 1043 | { 1044 | "kind": "SCALAR", 1045 | "name": "Int", 1046 | "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", 1047 | "fields": null, 1048 | "inputFields": null, 1049 | "interfaces": null, 1050 | "enumValues": null, 1051 | "possibleTypes": null 1052 | }, 1053 | { 1054 | "kind": "OBJECT", 1055 | "name": "ScheduleSlotConnection", 1056 | "description": "A connection to a list of items.", 1057 | "fields": [ 1058 | { 1059 | "name": "pageInfo", 1060 | "description": "Information to aid in pagination.", 1061 | "args": [], 1062 | "type": { 1063 | "kind": "NON_NULL", 1064 | "name": null, 1065 | "ofType": { 1066 | "kind": "OBJECT", 1067 | "name": "PageInfo", 1068 | "ofType": null 1069 | } 1070 | }, 1071 | "isDeprecated": false, 1072 | "deprecationReason": null 1073 | }, 1074 | { 1075 | "name": "edges", 1076 | "description": "Information to aid in pagination.", 1077 | "args": [], 1078 | "type": { 1079 | "kind": "LIST", 1080 | "name": null, 1081 | "ofType": { 1082 | "kind": "OBJECT", 1083 | "name": "ScheduleSlotEdge", 1084 | "ofType": null 1085 | } 1086 | }, 1087 | "isDeprecated": false, 1088 | "deprecationReason": null 1089 | } 1090 | ], 1091 | "inputFields": null, 1092 | "interfaces": [], 1093 | "enumValues": null, 1094 | "possibleTypes": null 1095 | }, 1096 | { 1097 | "kind": "OBJECT", 1098 | "name": "PageInfo", 1099 | "description": "Information about pagination in a connection.", 1100 | "fields": [ 1101 | { 1102 | "name": "hasNextPage", 1103 | "description": "When paginating forwards, are there more items?", 1104 | "args": [], 1105 | "type": { 1106 | "kind": "NON_NULL", 1107 | "name": null, 1108 | "ofType": { 1109 | "kind": "SCALAR", 1110 | "name": "Boolean", 1111 | "ofType": null 1112 | } 1113 | }, 1114 | "isDeprecated": false, 1115 | "deprecationReason": null 1116 | }, 1117 | { 1118 | "name": "hasPreviousPage", 1119 | "description": "When paginating backwards, are there more items?", 1120 | "args": [], 1121 | "type": { 1122 | "kind": "NON_NULL", 1123 | "name": null, 1124 | "ofType": { 1125 | "kind": "SCALAR", 1126 | "name": "Boolean", 1127 | "ofType": null 1128 | } 1129 | }, 1130 | "isDeprecated": false, 1131 | "deprecationReason": null 1132 | }, 1133 | { 1134 | "name": "startCursor", 1135 | "description": "When paginating backwards, the cursor to continue.", 1136 | "args": [], 1137 | "type": { 1138 | "kind": "SCALAR", 1139 | "name": "String", 1140 | "ofType": null 1141 | }, 1142 | "isDeprecated": false, 1143 | "deprecationReason": null 1144 | }, 1145 | { 1146 | "name": "endCursor", 1147 | "description": "When paginating forwards, the cursor to continue.", 1148 | "args": [], 1149 | "type": { 1150 | "kind": "SCALAR", 1151 | "name": "String", 1152 | "ofType": null 1153 | }, 1154 | "isDeprecated": false, 1155 | "deprecationReason": null 1156 | } 1157 | ], 1158 | "inputFields": null, 1159 | "interfaces": [], 1160 | "enumValues": null, 1161 | "possibleTypes": null 1162 | }, 1163 | { 1164 | "kind": "SCALAR", 1165 | "name": "Boolean", 1166 | "description": "The `Boolean` scalar type represents `true` or `false`.", 1167 | "fields": null, 1168 | "inputFields": null, 1169 | "interfaces": null, 1170 | "enumValues": null, 1171 | "possibleTypes": null 1172 | }, 1173 | { 1174 | "kind": "OBJECT", 1175 | "name": "ScheduleSlotEdge", 1176 | "description": "An edge in a connection.", 1177 | "fields": [ 1178 | { 1179 | "name": "node", 1180 | "description": "The item at the end of the edge", 1181 | "args": [], 1182 | "type": { 1183 | "kind": "UNION", 1184 | "name": "ScheduleSlot", 1185 | "ofType": null 1186 | }, 1187 | "isDeprecated": false, 1188 | "deprecationReason": null 1189 | }, 1190 | { 1191 | "name": "cursor", 1192 | "description": "A cursor for use in pagination", 1193 | "args": [], 1194 | "type": { 1195 | "kind": "NON_NULL", 1196 | "name": null, 1197 | "ofType": { 1198 | "kind": "SCALAR", 1199 | "name": "String", 1200 | "ofType": null 1201 | } 1202 | }, 1203 | "isDeprecated": false, 1204 | "deprecationReason": null 1205 | } 1206 | ], 1207 | "inputFields": null, 1208 | "interfaces": [], 1209 | "enumValues": null, 1210 | "possibleTypes": null 1211 | }, 1212 | { 1213 | "kind": "UNION", 1214 | "name": "ScheduleSlot", 1215 | "description": null, 1216 | "fields": null, 1217 | "inputFields": null, 1218 | "interfaces": null, 1219 | "enumValues": null, 1220 | "possibleTypes": [ 1221 | { 1222 | "kind": "OBJECT", 1223 | "name": "TalkSlot", 1224 | "ofType": null 1225 | }, 1226 | { 1227 | "kind": "OBJECT", 1228 | "name": "LightningTalksSlot", 1229 | "ofType": null 1230 | }, 1231 | { 1232 | "kind": "OBJECT", 1233 | "name": "ActivitySlot", 1234 | "ofType": null 1235 | } 1236 | ] 1237 | }, 1238 | { 1239 | "kind": "OBJECT", 1240 | "name": "User", 1241 | "description": "An authenticated user of the application.", 1242 | "fields": [ 1243 | { 1244 | "name": "id", 1245 | "description": "The ID of an object", 1246 | "args": [], 1247 | "type": { 1248 | "kind": "NON_NULL", 1249 | "name": null, 1250 | "ofType": { 1251 | "kind": "SCALAR", 1252 | "name": "ID", 1253 | "ofType": null 1254 | } 1255 | }, 1256 | "isDeprecated": false, 1257 | "deprecationReason": null 1258 | } 1259 | ], 1260 | "inputFields": null, 1261 | "interfaces": [ 1262 | { 1263 | "kind": "INTERFACE", 1264 | "name": "Node", 1265 | "ofType": null 1266 | } 1267 | ], 1268 | "enumValues": null, 1269 | "possibleTypes": null 1270 | }, 1271 | { 1272 | "kind": "OBJECT", 1273 | "name": "SpeakerConnection", 1274 | "description": "A connection to a list of items.", 1275 | "fields": [ 1276 | { 1277 | "name": "pageInfo", 1278 | "description": "Information to aid in pagination.", 1279 | "args": [], 1280 | "type": { 1281 | "kind": "NON_NULL", 1282 | "name": null, 1283 | "ofType": { 1284 | "kind": "OBJECT", 1285 | "name": "PageInfo", 1286 | "ofType": null 1287 | } 1288 | }, 1289 | "isDeprecated": false, 1290 | "deprecationReason": null 1291 | }, 1292 | { 1293 | "name": "edges", 1294 | "description": "Information to aid in pagination.", 1295 | "args": [], 1296 | "type": { 1297 | "kind": "LIST", 1298 | "name": null, 1299 | "ofType": { 1300 | "kind": "OBJECT", 1301 | "name": "SpeakerEdge", 1302 | "ofType": null 1303 | } 1304 | }, 1305 | "isDeprecated": false, 1306 | "deprecationReason": null 1307 | } 1308 | ], 1309 | "inputFields": null, 1310 | "interfaces": [], 1311 | "enumValues": null, 1312 | "possibleTypes": null 1313 | }, 1314 | { 1315 | "kind": "OBJECT", 1316 | "name": "SpeakerEdge", 1317 | "description": "An edge in a connection.", 1318 | "fields": [ 1319 | { 1320 | "name": "node", 1321 | "description": "The item at the end of the edge", 1322 | "args": [], 1323 | "type": { 1324 | "kind": "OBJECT", 1325 | "name": "Speaker", 1326 | "ofType": null 1327 | }, 1328 | "isDeprecated": false, 1329 | "deprecationReason": null 1330 | }, 1331 | { 1332 | "name": "cursor", 1333 | "description": "A cursor for use in pagination", 1334 | "args": [], 1335 | "type": { 1336 | "kind": "NON_NULL", 1337 | "name": null, 1338 | "ofType": { 1339 | "kind": "SCALAR", 1340 | "name": "String", 1341 | "ofType": null 1342 | } 1343 | }, 1344 | "isDeprecated": false, 1345 | "deprecationReason": null 1346 | } 1347 | ], 1348 | "inputFields": null, 1349 | "interfaces": [], 1350 | "enumValues": null, 1351 | "possibleTypes": null 1352 | }, 1353 | { 1354 | "kind": "OBJECT", 1355 | "name": "__Schema", 1356 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", 1357 | "fields": [ 1358 | { 1359 | "name": "types", 1360 | "description": "A list of all types supported by this server.", 1361 | "args": [], 1362 | "type": { 1363 | "kind": "NON_NULL", 1364 | "name": null, 1365 | "ofType": { 1366 | "kind": "LIST", 1367 | "name": null, 1368 | "ofType": { 1369 | "kind": "NON_NULL", 1370 | "name": null, 1371 | "ofType": { 1372 | "kind": "OBJECT", 1373 | "name": "__Type" 1374 | } 1375 | } 1376 | } 1377 | }, 1378 | "isDeprecated": false, 1379 | "deprecationReason": null 1380 | }, 1381 | { 1382 | "name": "queryType", 1383 | "description": "The type that query operations will be rooted at.", 1384 | "args": [], 1385 | "type": { 1386 | "kind": "NON_NULL", 1387 | "name": null, 1388 | "ofType": { 1389 | "kind": "OBJECT", 1390 | "name": "__Type", 1391 | "ofType": null 1392 | } 1393 | }, 1394 | "isDeprecated": false, 1395 | "deprecationReason": null 1396 | }, 1397 | { 1398 | "name": "mutationType", 1399 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 1400 | "args": [], 1401 | "type": { 1402 | "kind": "OBJECT", 1403 | "name": "__Type", 1404 | "ofType": null 1405 | }, 1406 | "isDeprecated": false, 1407 | "deprecationReason": null 1408 | }, 1409 | { 1410 | "name": "subscriptionType", 1411 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 1412 | "args": [], 1413 | "type": { 1414 | "kind": "OBJECT", 1415 | "name": "__Type", 1416 | "ofType": null 1417 | }, 1418 | "isDeprecated": false, 1419 | "deprecationReason": null 1420 | }, 1421 | { 1422 | "name": "directives", 1423 | "description": "A list of all directives supported by this server.", 1424 | "args": [], 1425 | "type": { 1426 | "kind": "NON_NULL", 1427 | "name": null, 1428 | "ofType": { 1429 | "kind": "LIST", 1430 | "name": null, 1431 | "ofType": { 1432 | "kind": "NON_NULL", 1433 | "name": null, 1434 | "ofType": { 1435 | "kind": "OBJECT", 1436 | "name": "__Directive" 1437 | } 1438 | } 1439 | } 1440 | }, 1441 | "isDeprecated": false, 1442 | "deprecationReason": null 1443 | } 1444 | ], 1445 | "inputFields": null, 1446 | "interfaces": [], 1447 | "enumValues": null, 1448 | "possibleTypes": null 1449 | }, 1450 | { 1451 | "kind": "OBJECT", 1452 | "name": "__Type", 1453 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", 1454 | "fields": [ 1455 | { 1456 | "name": "kind", 1457 | "description": null, 1458 | "args": [], 1459 | "type": { 1460 | "kind": "NON_NULL", 1461 | "name": null, 1462 | "ofType": { 1463 | "kind": "ENUM", 1464 | "name": "__TypeKind", 1465 | "ofType": null 1466 | } 1467 | }, 1468 | "isDeprecated": false, 1469 | "deprecationReason": null 1470 | }, 1471 | { 1472 | "name": "name", 1473 | "description": null, 1474 | "args": [], 1475 | "type": { 1476 | "kind": "SCALAR", 1477 | "name": "String", 1478 | "ofType": null 1479 | }, 1480 | "isDeprecated": false, 1481 | "deprecationReason": null 1482 | }, 1483 | { 1484 | "name": "description", 1485 | "description": null, 1486 | "args": [], 1487 | "type": { 1488 | "kind": "SCALAR", 1489 | "name": "String", 1490 | "ofType": null 1491 | }, 1492 | "isDeprecated": false, 1493 | "deprecationReason": null 1494 | }, 1495 | { 1496 | "name": "fields", 1497 | "description": null, 1498 | "args": [ 1499 | { 1500 | "name": "includeDeprecated", 1501 | "description": null, 1502 | "type": { 1503 | "kind": "SCALAR", 1504 | "name": "Boolean", 1505 | "ofType": null 1506 | }, 1507 | "defaultValue": "false" 1508 | } 1509 | ], 1510 | "type": { 1511 | "kind": "LIST", 1512 | "name": null, 1513 | "ofType": { 1514 | "kind": "NON_NULL", 1515 | "name": null, 1516 | "ofType": { 1517 | "kind": "OBJECT", 1518 | "name": "__Field", 1519 | "ofType": null 1520 | } 1521 | } 1522 | }, 1523 | "isDeprecated": false, 1524 | "deprecationReason": null 1525 | }, 1526 | { 1527 | "name": "interfaces", 1528 | "description": null, 1529 | "args": [], 1530 | "type": { 1531 | "kind": "LIST", 1532 | "name": null, 1533 | "ofType": { 1534 | "kind": "NON_NULL", 1535 | "name": null, 1536 | "ofType": { 1537 | "kind": "OBJECT", 1538 | "name": "__Type", 1539 | "ofType": null 1540 | } 1541 | } 1542 | }, 1543 | "isDeprecated": false, 1544 | "deprecationReason": null 1545 | }, 1546 | { 1547 | "name": "possibleTypes", 1548 | "description": null, 1549 | "args": [], 1550 | "type": { 1551 | "kind": "LIST", 1552 | "name": null, 1553 | "ofType": { 1554 | "kind": "NON_NULL", 1555 | "name": null, 1556 | "ofType": { 1557 | "kind": "OBJECT", 1558 | "name": "__Type", 1559 | "ofType": null 1560 | } 1561 | } 1562 | }, 1563 | "isDeprecated": false, 1564 | "deprecationReason": null 1565 | }, 1566 | { 1567 | "name": "enumValues", 1568 | "description": null, 1569 | "args": [ 1570 | { 1571 | "name": "includeDeprecated", 1572 | "description": null, 1573 | "type": { 1574 | "kind": "SCALAR", 1575 | "name": "Boolean", 1576 | "ofType": null 1577 | }, 1578 | "defaultValue": "false" 1579 | } 1580 | ], 1581 | "type": { 1582 | "kind": "LIST", 1583 | "name": null, 1584 | "ofType": { 1585 | "kind": "NON_NULL", 1586 | "name": null, 1587 | "ofType": { 1588 | "kind": "OBJECT", 1589 | "name": "__EnumValue", 1590 | "ofType": null 1591 | } 1592 | } 1593 | }, 1594 | "isDeprecated": false, 1595 | "deprecationReason": null 1596 | }, 1597 | { 1598 | "name": "inputFields", 1599 | "description": null, 1600 | "args": [], 1601 | "type": { 1602 | "kind": "LIST", 1603 | "name": null, 1604 | "ofType": { 1605 | "kind": "NON_NULL", 1606 | "name": null, 1607 | "ofType": { 1608 | "kind": "OBJECT", 1609 | "name": "__InputValue", 1610 | "ofType": null 1611 | } 1612 | } 1613 | }, 1614 | "isDeprecated": false, 1615 | "deprecationReason": null 1616 | }, 1617 | { 1618 | "name": "ofType", 1619 | "description": null, 1620 | "args": [], 1621 | "type": { 1622 | "kind": "OBJECT", 1623 | "name": "__Type", 1624 | "ofType": null 1625 | }, 1626 | "isDeprecated": false, 1627 | "deprecationReason": null 1628 | } 1629 | ], 1630 | "inputFields": null, 1631 | "interfaces": [], 1632 | "enumValues": null, 1633 | "possibleTypes": null 1634 | }, 1635 | { 1636 | "kind": "ENUM", 1637 | "name": "__TypeKind", 1638 | "description": "An enum describing what kind of type a given `__Type` is.", 1639 | "fields": null, 1640 | "inputFields": null, 1641 | "interfaces": null, 1642 | "enumValues": [ 1643 | { 1644 | "name": "SCALAR", 1645 | "description": "Indicates this type is a scalar.", 1646 | "isDeprecated": false, 1647 | "deprecationReason": null 1648 | }, 1649 | { 1650 | "name": "OBJECT", 1651 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 1652 | "isDeprecated": false, 1653 | "deprecationReason": null 1654 | }, 1655 | { 1656 | "name": "INTERFACE", 1657 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 1658 | "isDeprecated": false, 1659 | "deprecationReason": null 1660 | }, 1661 | { 1662 | "name": "UNION", 1663 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 1664 | "isDeprecated": false, 1665 | "deprecationReason": null 1666 | }, 1667 | { 1668 | "name": "ENUM", 1669 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 1670 | "isDeprecated": false, 1671 | "deprecationReason": null 1672 | }, 1673 | { 1674 | "name": "INPUT_OBJECT", 1675 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 1676 | "isDeprecated": false, 1677 | "deprecationReason": null 1678 | }, 1679 | { 1680 | "name": "LIST", 1681 | "description": "Indicates this type is a list. `ofType` is a valid field.", 1682 | "isDeprecated": false, 1683 | "deprecationReason": null 1684 | }, 1685 | { 1686 | "name": "NON_NULL", 1687 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 1688 | "isDeprecated": false, 1689 | "deprecationReason": null 1690 | } 1691 | ], 1692 | "possibleTypes": null 1693 | }, 1694 | { 1695 | "kind": "OBJECT", 1696 | "name": "__Field", 1697 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", 1698 | "fields": [ 1699 | { 1700 | "name": "name", 1701 | "description": null, 1702 | "args": [], 1703 | "type": { 1704 | "kind": "NON_NULL", 1705 | "name": null, 1706 | "ofType": { 1707 | "kind": "SCALAR", 1708 | "name": "String", 1709 | "ofType": null 1710 | } 1711 | }, 1712 | "isDeprecated": false, 1713 | "deprecationReason": null 1714 | }, 1715 | { 1716 | "name": "description", 1717 | "description": null, 1718 | "args": [], 1719 | "type": { 1720 | "kind": "SCALAR", 1721 | "name": "String", 1722 | "ofType": null 1723 | }, 1724 | "isDeprecated": false, 1725 | "deprecationReason": null 1726 | }, 1727 | { 1728 | "name": "args", 1729 | "description": null, 1730 | "args": [], 1731 | "type": { 1732 | "kind": "NON_NULL", 1733 | "name": null, 1734 | "ofType": { 1735 | "kind": "LIST", 1736 | "name": null, 1737 | "ofType": { 1738 | "kind": "NON_NULL", 1739 | "name": null, 1740 | "ofType": { 1741 | "kind": "OBJECT", 1742 | "name": "__InputValue" 1743 | } 1744 | } 1745 | } 1746 | }, 1747 | "isDeprecated": false, 1748 | "deprecationReason": null 1749 | }, 1750 | { 1751 | "name": "type", 1752 | "description": null, 1753 | "args": [], 1754 | "type": { 1755 | "kind": "NON_NULL", 1756 | "name": null, 1757 | "ofType": { 1758 | "kind": "OBJECT", 1759 | "name": "__Type", 1760 | "ofType": null 1761 | } 1762 | }, 1763 | "isDeprecated": false, 1764 | "deprecationReason": null 1765 | }, 1766 | { 1767 | "name": "isDeprecated", 1768 | "description": null, 1769 | "args": [], 1770 | "type": { 1771 | "kind": "NON_NULL", 1772 | "name": null, 1773 | "ofType": { 1774 | "kind": "SCALAR", 1775 | "name": "Boolean", 1776 | "ofType": null 1777 | } 1778 | }, 1779 | "isDeprecated": false, 1780 | "deprecationReason": null 1781 | }, 1782 | { 1783 | "name": "deprecationReason", 1784 | "description": null, 1785 | "args": [], 1786 | "type": { 1787 | "kind": "SCALAR", 1788 | "name": "String", 1789 | "ofType": null 1790 | }, 1791 | "isDeprecated": false, 1792 | "deprecationReason": null 1793 | } 1794 | ], 1795 | "inputFields": null, 1796 | "interfaces": [], 1797 | "enumValues": null, 1798 | "possibleTypes": null 1799 | }, 1800 | { 1801 | "kind": "OBJECT", 1802 | "name": "__InputValue", 1803 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", 1804 | "fields": [ 1805 | { 1806 | "name": "name", 1807 | "description": null, 1808 | "args": [], 1809 | "type": { 1810 | "kind": "NON_NULL", 1811 | "name": null, 1812 | "ofType": { 1813 | "kind": "SCALAR", 1814 | "name": "String", 1815 | "ofType": null 1816 | } 1817 | }, 1818 | "isDeprecated": false, 1819 | "deprecationReason": null 1820 | }, 1821 | { 1822 | "name": "description", 1823 | "description": null, 1824 | "args": [], 1825 | "type": { 1826 | "kind": "SCALAR", 1827 | "name": "String", 1828 | "ofType": null 1829 | }, 1830 | "isDeprecated": false, 1831 | "deprecationReason": null 1832 | }, 1833 | { 1834 | "name": "type", 1835 | "description": null, 1836 | "args": [], 1837 | "type": { 1838 | "kind": "NON_NULL", 1839 | "name": null, 1840 | "ofType": { 1841 | "kind": "OBJECT", 1842 | "name": "__Type", 1843 | "ofType": null 1844 | } 1845 | }, 1846 | "isDeprecated": false, 1847 | "deprecationReason": null 1848 | }, 1849 | { 1850 | "name": "defaultValue", 1851 | "description": "A GraphQL-formatted string representing the default value for this input value.", 1852 | "args": [], 1853 | "type": { 1854 | "kind": "SCALAR", 1855 | "name": "String", 1856 | "ofType": null 1857 | }, 1858 | "isDeprecated": false, 1859 | "deprecationReason": null 1860 | } 1861 | ], 1862 | "inputFields": null, 1863 | "interfaces": [], 1864 | "enumValues": null, 1865 | "possibleTypes": null 1866 | }, 1867 | { 1868 | "kind": "OBJECT", 1869 | "name": "__EnumValue", 1870 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", 1871 | "fields": [ 1872 | { 1873 | "name": "name", 1874 | "description": null, 1875 | "args": [], 1876 | "type": { 1877 | "kind": "NON_NULL", 1878 | "name": null, 1879 | "ofType": { 1880 | "kind": "SCALAR", 1881 | "name": "String", 1882 | "ofType": null 1883 | } 1884 | }, 1885 | "isDeprecated": false, 1886 | "deprecationReason": null 1887 | }, 1888 | { 1889 | "name": "description", 1890 | "description": null, 1891 | "args": [], 1892 | "type": { 1893 | "kind": "SCALAR", 1894 | "name": "String", 1895 | "ofType": null 1896 | }, 1897 | "isDeprecated": false, 1898 | "deprecationReason": null 1899 | }, 1900 | { 1901 | "name": "isDeprecated", 1902 | "description": null, 1903 | "args": [], 1904 | "type": { 1905 | "kind": "NON_NULL", 1906 | "name": null, 1907 | "ofType": { 1908 | "kind": "SCALAR", 1909 | "name": "Boolean", 1910 | "ofType": null 1911 | } 1912 | }, 1913 | "isDeprecated": false, 1914 | "deprecationReason": null 1915 | }, 1916 | { 1917 | "name": "deprecationReason", 1918 | "description": null, 1919 | "args": [], 1920 | "type": { 1921 | "kind": "SCALAR", 1922 | "name": "String", 1923 | "ofType": null 1924 | }, 1925 | "isDeprecated": false, 1926 | "deprecationReason": null 1927 | } 1928 | ], 1929 | "inputFields": null, 1930 | "interfaces": [], 1931 | "enumValues": null, 1932 | "possibleTypes": null 1933 | }, 1934 | { 1935 | "kind": "OBJECT", 1936 | "name": "__Directive", 1937 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL’s execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", 1938 | "fields": [ 1939 | { 1940 | "name": "name", 1941 | "description": null, 1942 | "args": [], 1943 | "type": { 1944 | "kind": "NON_NULL", 1945 | "name": null, 1946 | "ofType": { 1947 | "kind": "SCALAR", 1948 | "name": "String", 1949 | "ofType": null 1950 | } 1951 | }, 1952 | "isDeprecated": false, 1953 | "deprecationReason": null 1954 | }, 1955 | { 1956 | "name": "description", 1957 | "description": null, 1958 | "args": [], 1959 | "type": { 1960 | "kind": "SCALAR", 1961 | "name": "String", 1962 | "ofType": null 1963 | }, 1964 | "isDeprecated": false, 1965 | "deprecationReason": null 1966 | }, 1967 | { 1968 | "name": "args", 1969 | "description": null, 1970 | "args": [], 1971 | "type": { 1972 | "kind": "NON_NULL", 1973 | "name": null, 1974 | "ofType": { 1975 | "kind": "LIST", 1976 | "name": null, 1977 | "ofType": { 1978 | "kind": "NON_NULL", 1979 | "name": null, 1980 | "ofType": { 1981 | "kind": "OBJECT", 1982 | "name": "__InputValue" 1983 | } 1984 | } 1985 | } 1986 | }, 1987 | "isDeprecated": false, 1988 | "deprecationReason": null 1989 | }, 1990 | { 1991 | "name": "onOperation", 1992 | "description": null, 1993 | "args": [], 1994 | "type": { 1995 | "kind": "NON_NULL", 1996 | "name": null, 1997 | "ofType": { 1998 | "kind": "SCALAR", 1999 | "name": "Boolean", 2000 | "ofType": null 2001 | } 2002 | }, 2003 | "isDeprecated": false, 2004 | "deprecationReason": null 2005 | }, 2006 | { 2007 | "name": "onFragment", 2008 | "description": null, 2009 | "args": [], 2010 | "type": { 2011 | "kind": "NON_NULL", 2012 | "name": null, 2013 | "ofType": { 2014 | "kind": "SCALAR", 2015 | "name": "Boolean", 2016 | "ofType": null 2017 | } 2018 | }, 2019 | "isDeprecated": false, 2020 | "deprecationReason": null 2021 | }, 2022 | { 2023 | "name": "onField", 2024 | "description": null, 2025 | "args": [], 2026 | "type": { 2027 | "kind": "NON_NULL", 2028 | "name": null, 2029 | "ofType": { 2030 | "kind": "SCALAR", 2031 | "name": "Boolean", 2032 | "ofType": null 2033 | } 2034 | }, 2035 | "isDeprecated": false, 2036 | "deprecationReason": null 2037 | } 2038 | ], 2039 | "inputFields": null, 2040 | "interfaces": [], 2041 | "enumValues": null, 2042 | "possibleTypes": null 2043 | } 2044 | ], 2045 | "directives": [ 2046 | { 2047 | "name": "include", 2048 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 2049 | "args": [ 2050 | { 2051 | "name": "if", 2052 | "description": "Included when true.", 2053 | "type": { 2054 | "kind": "NON_NULL", 2055 | "name": null, 2056 | "ofType": { 2057 | "kind": "SCALAR", 2058 | "name": "Boolean", 2059 | "ofType": null 2060 | } 2061 | }, 2062 | "defaultValue": null 2063 | } 2064 | ], 2065 | "onOperation": false, 2066 | "onFragment": true, 2067 | "onField": true 2068 | }, 2069 | { 2070 | "name": "skip", 2071 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 2072 | "args": [ 2073 | { 2074 | "name": "if", 2075 | "description": "Skipped when true.", 2076 | "type": { 2077 | "kind": "NON_NULL", 2078 | "name": null, 2079 | "ofType": { 2080 | "kind": "SCALAR", 2081 | "name": "Boolean", 2082 | "ofType": null 2083 | } 2084 | }, 2085 | "defaultValue": null 2086 | } 2087 | ], 2088 | "onOperation": false, 2089 | "onFragment": true, 2090 | "onField": true 2091 | } 2092 | ] 2093 | } 2094 | } 2095 | } --------------------------------------------------------------------------------