[
  {
    "path": ".babelrc",
    "content": "{\n  \"plugins\": [\"relay\"],\n  \"presets\": [\"react-native\"]\n}\n"
  },
  {
    "path": ".buckconfig",
    "content": "\n[android]\n  target = Google Inc.:Google APIs:23\n\n[maven_repositories]\n  central = https://repo1.maven.org/maven2\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n    working_directory: ~/f8app\n    docker:\n      - image: circleci/node:7.10\n    steps:\n      - checkout\n      - restore_cache:\n          keys:\n          - v1-dependencies-{{ checksum \"package.json\" }}\n          # fallback to using the latest cache if no exact match is found\n          - v1-dependencies-\n      - run: yarn install\n      - save_cache:\n          paths:\n            - node_modules\n          key: v1-dependencies-{{ checksum \"package.json\" }}\n      - run: yarn lint\n      - run: yarn flow\n      - run: yarn test\n\n  android:\n    working_directory: ~/f8app\n    docker:\n      - image: circleci/android:api-27-node8-alpha\n    steps:\n      - checkout\n      - run: yarn install\n      - run: cd android && ./gradlew assembleDebug\n\nworkflows:\n  version: 2\n  build_and_test:\n    jobs:\n      - build\n      - android\n"
  },
  {
    "path": ".eslintignore",
    "content": "**/node_modules/**\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parser\": \"babel-eslint\",\n\n  \"ecmaFeatures\": {\n    \"jsx\": true\n  },\n\n  \"env\": {\n    \"es6\": true,\n    \"jasmine\": true,\n  },\n\n  \"plugins\": [\n    \"react\",\n    \"prettier\",\n  ],\n\n  // Map from global var to bool specifying if it can be redefined\n  \"globals\": {\n    \"__DEV__\": true,\n    \"__dirname\": false,\n    \"__fbBatchedBridgeConfig\": false,\n    \"alert\": false,\n    \"cancelAnimationFrame\": false,\n    \"cancelIdleCallback\": false,\n    \"clearImmediate\": true,\n    \"clearInterval\": false,\n    \"clearTimeout\": false,\n    \"console\": false,\n    \"document\": false,\n    \"escape\": false,\n    \"Event\": false,\n    \"EventTarget\": false,\n    \"exports\": false,\n    \"fetch\": false,\n    \"FormData\": false,\n    \"global\": false,\n    \"jest\": false,\n    \"Map\": true,\n    \"module\": false,\n    \"navigator\": false,\n    \"process\": false,\n    \"Promise\": true,\n    \"requestAnimationFrame\": true,\n    \"requestIdleCallback\": true,\n    \"require\": false,\n    \"Set\": true,\n    \"setImmediate\": true,\n    \"setInterval\": false,\n    \"setTimeout\": false,\n    \"window\": false,\n    \"XMLHttpRequest\": false,\n    \"pit\": false,\n\n    // Flow global types.\n    \"ReactComponent\": false,\n    \"ReactClass\": false,\n    \"ReactElement\": false,\n    \"ReactPropsCheckType\": false,\n    \"ReactPropsChainableTypeChecker\": false,\n    \"ReactPropTypes\": false,\n    \"SyntheticEvent\": false,\n    \"$Either\": false,\n    \"$All\": false,\n    \"$ArrayBufferView\": false,\n    \"$Tuple\": false,\n    \"$Supertype\": false,\n    \"$Subtype\": false,\n    \"$Shape\": false,\n    \"$Diff\": false,\n    \"$Keys\": false,\n    \"$Enum\": false,\n    \"$Exports\": false,\n    \"$FlowIssue\": false,\n    \"$FlowFixMe\": false,\n    \"$FixMe\": false\n  },\n\n  \"rules\": {\n    \"comma-dangle\": 0,               // disallow trailing commas in object literals\n    \"no-cond-assign\": 1,             // disallow assignment in conditional expressions\n    \"no-console\": 0,                 // disallow use of console (off by default in the node environment)\n    \"no-const-assign\": 2,            // disallow assignment to const-declared variables\n    \"no-constant-condition\": 0,      // disallow use of constant expressions in conditions\n    \"no-control-regex\": 1,           // disallow control characters in regular expressions\n    \"no-debugger\": 1,                // disallow use of debugger\n    \"no-dupe-keys\": 1,               // disallow duplicate keys when creating object literals\n    \"no-empty\": 0,                   // disallow empty statements\n    \"no-ex-assign\": 1,               // disallow assigning to the exception in a catch block\n    \"no-extra-boolean-cast\": 1,      // disallow double-negation boolean casts in a boolean context\n    \"no-extra-parens\": 0,            // disallow unnecessary parentheses (off by default)\n    \"no-extra-semi\": 1,              // disallow unnecessary semicolons\n    \"no-func-assign\": 1,             // disallow overwriting functions written as function declarations\n    \"no-inner-declarations\": 0,      // disallow function or variable declarations in nested blocks\n    \"no-invalid-regexp\": 1,          // disallow invalid regular expression strings in the RegExp constructor\n    \"no-negated-in-lhs\": 1,          // disallow negation of the left operand of an in expression\n    \"no-obj-calls\": 1,               // disallow the use of object properties of the global object (Math and JSON) as functions\n    \"no-regex-spaces\": 1,            // disallow multiple spaces in a regular expression literal\n    \"no-reserved-keys\": 0,           // disallow reserved words being used as object literal keys (off by default)\n    \"no-sparse-arrays\": 1,           // disallow sparse arrays\n    \"no-unreachable\": 1,             // disallow unreachable statements after a return, throw, continue, or break statement\n    \"use-isnan\": 1,                  // disallow comparisons with the value NaN\n    \"valid-jsdoc\": 0,                // Ensure JSDoc comments are valid (off by default)\n    \"valid-typeof\": 1,               // Ensure that the results of typeof are compared against a valid string\n    \"no-var\": 1,                     // discouraging the use of var and encouraging the use of const or let instead\n\n  // Best Practices\n  // These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns.\n\n    \"block-scoped-var\": 0,           // treat var statements as if they were block scoped (off by default)\n    \"complexity\": 0,                 // specify the maximum cyclomatic complexity allowed in a program (off by default)\n    \"consistent-return\": 0,          // require return statements to either always or never specify values\n    \"curly\": 1,                      // specify curly brace conventions for all control statements\n    \"default-case\": 0,               // require default case in switch statements (off by default)\n    \"dot-notation\": 1,               // encourages use of dot notation whenever possible\n    \"eqeqeq\": [1, \"allow-null\"],     // require the use of === and !==\n    \"guard-for-in\": 0,               // make sure for-in loops have an if statement (off by default)\n    \"no-alert\": 1,                   // disallow the use of alert, confirm, and prompt\n    \"no-caller\": 1,                  // disallow use of arguments.caller or arguments.callee\n    \"no-div-regex\": 1,               // disallow division operators explicitly at beginning of regular expression (off by default)\n    \"no-else-return\": 0,             // disallow else after a return in an if (off by default)\n    \"no-eq-null\": 0,                 // disallow comparisons to null without a type-checking operator (off by default)\n    \"no-eval\": 1,                    // disallow use of eval()\n    \"no-extend-native\": 1,           // disallow adding to native types\n    \"no-extra-bind\": 1,              // disallow unnecessary function binding\n    \"no-fallthrough\": 1,             // disallow fallthrough of case statements\n    \"no-floating-decimal\": 1,        // disallow the use of leading or trailing decimal points in numeric literals (off by default)\n    \"no-implied-eval\": 1,            // disallow use of eval()-like methods\n    \"no-labels\": 1,                  // disallow use of labeled statements\n    \"no-iterator\": 1,                // disallow usage of __iterator__ property\n    \"no-lone-blocks\": 1,             // disallow unnecessary nested blocks\n    \"no-loop-func\": 0,               // disallow creation of functions within loops\n    \"no-multi-str\": 0,               // disallow use of multiline strings\n    \"no-native-reassign\": 0,         // disallow reassignments of native objects\n    \"no-new\": 1,                     // disallow use of new operator when not part of the assignment or comparison\n    \"no-new-func\": 1,                // disallow use of new operator for Function object\n    \"no-new-wrappers\": 1,            // disallows creating new instances of String,Number, and Boolean\n    \"no-octal\": 1,                   // disallow use of octal literals\n    \"no-octal-escape\": 1,            // disallow use of octal escape sequences in string literals, such as var foo = \"Copyright \\251\";\n    \"no-proto\": 1,                   // disallow usage of __proto__ property\n    \"no-redeclare\": 0,               // disallow declaring the same variable more then once\n    \"no-return-assign\": 1,           // disallow use of assignment in return statement\n    \"no-script-url\": 1,              // disallow use of javascript: urls.\n    \"no-self-compare\": 1,            // disallow comparisons where both sides are exactly the same (off by default)\n    \"no-sequences\": 1,               // disallow use of comma operator\n    \"no-unused-expressions\": 0,      // disallow usage of expressions in statement position\n    \"no-void\": 1,                    // disallow use of void operator (off by default)\n    \"no-warning-comments\": 0,        // disallow usage of configurable warning terms in comments\": 1,                        // e.g. TODO or FIXME (off by default)\n    \"no-with\": 1,                    // disallow use of the with statement\n    \"radix\": 1,                      // require use of the second argument for parseInt() (off by default)\n    \"semi-spacing\": 1,\t             // require a space after a semi-colon\n    \"vars-on-top\": 0,                // requires to declare all vars on top of their containing scope (off by default)\n    \"wrap-iife\": 0,                  // require immediate function invocation to be wrapped in parentheses (off by default)\n    \"yoda\": 1,                       // require or disallow Yoda conditions\n\n  // Variables\n  // These rules have to do with variable declarations.\n\n    \"no-catch-shadow\": 1,            // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment)\n    \"no-delete-var\": 1,              // disallow deletion of variables\n    \"no-label-var\": 1,               // disallow labels that share a name with a variable\n    \"no-shadow\": 1,                  // disallow declaration of variables already declared in the outer scope\n    \"no-shadow-restricted-names\": 1, // disallow shadowing of names such as arguments\n    \"no-undef\": 2,                   // disallow use of undeclared variables unless mentioned in a /*global */ block\n    \"no-undefined\": 0,               // disallow use of undefined variable (off by default)\n    \"no-undef-init\": 1,              // disallow use of undefined when initializing variables\n    \"no-unused-vars\": [1, {\"vars\": \"all\", \"args\": \"none\"}], // disallow declaration of variables that are not used in the code\n    \"no-use-before-define\": 0,       // disallow use of variables before they are defined\n\n  // Node.js\n  // These rules are specific to JavaScript running on Node.js.\n\n    \"handle-callback-err\": 1,        // enforces error handling in callbacks (off by default) (on by default in the node environment)\n    \"no-mixed-requires\": 1,          // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment)\n    \"no-new-require\": 1,             // disallow use of new operator with the require function (off by default) (on by default in the node environment)\n    \"no-path-concat\": 1,             // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment)\n    \"no-process-exit\": 0,            // disallow process.exit() (on by default in the node environment)\n    \"no-restricted-modules\": 1,      // restrict usage of specified node modules (off by default)\n    \"no-sync\": 0,                    // disallow use of synchronous methods (off by default)\n\n  // Stylistic Issues\n  // These rules are purely matters of style and are quite subjective.\n\n    \"key-spacing\": 0,\n    \"keyword-spacing\": 1,            // enforce spacing before and after keywords\n    \"jsx-quotes\": [1, \"prefer-double\"],\n    \"comma-spacing\": 0,\n    \"no-multi-spaces\": 0,\n    \"brace-style\": 0,                // enforce one true brace style (off by default)\n    \"camelcase\": 0,                  // require camel case names\n    \"consistent-this\": [1, \"self\"],            // enforces consistent naming when capturing the current execution context (off by default)\n    \"eol-last\": 1,                   // enforce newline at the end of file, with no multiple empty lines\n    \"func-names\": 0,                 // require function expressions to have a name (off by default)\n    \"func-style\": 0,                 // enforces use of function declarations or expressions (off by default)\n    \"new-cap\": 0,                    // require a capital letter for constructors\n    \"new-parens\": 1,                 // disallow the omission of parentheses when invoking a constructor with no arguments\n    \"no-nested-ternary\": 0,          // disallow nested ternary expressions (off by default)\n    \"no-array-constructor\": 1,       // disallow use of the Array constructor\n    \"no-lonely-if\": 0,               // disallow if as the only statement in an else block (off by default)\n    \"no-new-object\": 1,              // disallow use of the Object constructor\n    \"no-spaced-func\": 1,             // disallow space between function identifier and application\n    \"no-ternary\": 0,                 // disallow the use of ternary operators (off by default)\n    \"no-trailing-spaces\": 1,         // disallow trailing whitespace at the end of lines\n    \"no-underscore-dangle\": 0,       // disallow dangling underscores in identifiers\n    \"no-mixed-spaces-and-tabs\": 1,   // disallow mixed spaces and tabs for indentation\n    \"quotes\": [1, \"double\", \"avoid-escape\"], // specify whether double or single quotes should be used\n    \"quote-props\": 0,                // require quotes around object literal property names (off by default)\n    \"semi\": 1,                       // require or disallow use of semicolons instead of ASI\n    \"sort-vars\": 0,                  // sort variables within the same declaration block (off by default)\n    \"space-in-brackets\": 0,          // require or disallow spaces inside brackets (off by default)\n    \"space-in-parens\": 0,            // require or disallow spaces inside parentheses (off by default)\n    \"space-infix-ops\": 1,            // require spaces around operators\n    \"space-unary-ops\": [1, { \"words\": true, \"nonwords\": false }], // require or disallow spaces before/after unary operators (words on by default, nonwords off by default)\n    \"max-nested-callbacks\": 0,       // specify the maximum depth callbacks can be nested (off by default)\n    \"one-var\": 0,                    // allow just one var statement per function (off by default)\n    \"wrap-regex\": 0,                 // require regex literals to be wrapped in parentheses (off by default)\n\n  // Legacy\n  // The following rules are included for compatibility with JSHint and JSLint. While the names of the rules may not match up with the JSHint/JSLint counterpart, the functionality is the same.\n\n    \"max-depth\": 0,                  // specify the maximum depth that blocks can be nested (off by default)\n    \"max-len\": 0,                    // specify the maximum length of a line in your program (off by default)\n    \"max-params\": 0,                 // limits the number of parameters that can be used in the function declaration. (off by default)\n    \"max-statements\": 0,             // specify the maximum number of statement allowed in a function (off by default)\n    \"no-bitwise\": 1,                 // disallow use of bitwise operators (off by default)\n    \"no-plusplus\": 0,                // disallow use of unary operators, ++ and -- (off by default)\n\n  // React Plugin\n  // The following rules are made available via `eslint-plugin-react`.\n\n    \"react/display-name\": 0,\n    \"react/jsx-boolean-value\": 0,\n    \"react/jsx-no-duplicate-props\": 2,\n    \"react/jsx-no-undef\": 1,\n    \"react/jsx-sort-props\": 0,\n    \"react/jsx-uses-react\": 1,\n    \"react/jsx-uses-vars\": 1,\n    \"react/no-did-mount-set-state\": 1,\n    \"react/no-did-update-set-state\": 1,\n    \"react/no-multi-comp\": 0,\n    \"react/no-string-refs\": 1,\n    \"react/no-unknown-property\": 0,\n    \"react/prop-types\": 0,\n    \"react/react-in-jsx-scope\": 1,\n    \"react/self-closing-comp\": 1,\n    \"react/wrap-multilines\": 0,\n\n  // Prettier Plugin\n  // The following rules are made available via `eslint-plugin-prettier`.\n    \"prettier/prettier\": \"error\",\n  }\n}\n"
  },
  {
    "path": ".flowconfig",
    "content": "[ignore]\n; We fork some components by platform\n.*/*[.]android.js\n\n; Ignore \"BUCK\" generated dirs\n<PROJECT_ROOT>/\\.buckd/\n\n; Ignore unexpected extra \"@providesModule\"\n.*/node_modules/.*/node_modules/fbjs/.*\n\n; Ignore unrelated errors in node_modules\n.*/react-native/Libraries/.*\n.*/react-native/lib/.*\n.*/babel-plugin-relay/lib/.*\n.*/react-relay/lib/.*\n.*/relay-compiler/lib/.*\n.*/relay-runtime/lib/.*\n.*/graphql/.*\n.*/react-native-keyboard-aware-scroll-view/.*\n.*/react-native-linear-gradient/.*\n.*/react-native-deprecated-custom-components/src/flattenStyle.js\n\n[include]\n\n[libs]\nnode_modules/react-native/Libraries/react-native/react-native-interface.js\nnode_modules/react-native/flow/\nflow/\njs/flow-lib.js\n\n[options]\nemoji=true\n\nmodule.system=haste\n\nexperimental.strict_type_args=true\n\nmunge_underscores=true\n\nmodule.name_mapper.extension='\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\)$' -> 'RelativeImageStub'\n\nsuppress_type=$FlowIssue\nsuppress_type=$FlowFixMe\nsuppress_type=$FixMe\n\nsuppress_comment=\\\\(.\\\\|\\n\\\\)*\\\\$FlowFixMe\\\\($\\\\|[^(]\\\\|(\\\\(>=0\\\\.\\\\(3[0-8]\\\\|[1-2][0-9]\\\\|[0-9]\\\\).[0-9]\\\\)? *\\\\(site=[a-z,_]*react_native[a-z,_]*\\\\)?)\\\\)\nsuppress_comment=\\\\(.\\\\|\\n\\\\)*\\\\$FlowIssue\\\\((\\\\(>=0\\\\.\\\\(3[0-8]\\\\|1[0-9]\\\\|[1-2][0-9]\\\\).[0-9]\\\\)? *\\\\(site=[a-z,_]*react_native[a-z,_]*\\\\)?)\\\\)?:? #[0-9]+\nsuppress_comment=\\\\(.\\\\|\\n\\\\)*\\\\$FlowFixedInNextDeploy\n\nunsafe.enable_getters_and_setters=true\n\n[version]\n^0.42.0\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.pbxproj -text\n"
  },
  {
    "path": ".gitignore",
    "content": "# OSX\n#\n.DS_Store\n\n# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n*.xccheckout\n*.moved-aside\nDerivedData\n*.hmap\n*.ipa\n*.xcuserstate\nproject.xcworkspace\n\n# Android/IntelliJ\n#\nbuild/\n.idea\n.gradle\nlocal.properties\n*.iml\n\n# node.js\n#\nnode_modules/\nnpm-debug.log\nyarn-error.log\npackage-lock.json\n\n# BUCK\nbuck-out/\n\\.buckd/\n*.keystore\n\n# fastlane\n#\n# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the\n# screenshots whenever they are needed.\n# For more information about the recommended setup visit:\n# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md\n\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots\n"
  },
  {
    "path": ".watchmanconfig",
    "content": "{}"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nFacebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to F8 App\n\nWe want to make contributing to this project as easy and transparent as\npossible.\n\n## Code of Conduct\n\nThe code of conduct is described in [`CODE_OF_CONDUCT.md`](/CODE_OF_CONDUCT.md)\n\n## Get involved\n\nThere are many ways to contribute to this project, and many of them do not involve writing any code. Here's a few ideas to get started:\n\n* Go through the [Getting Started](http://makeitopen.com/docs/en/1-A1-local-setup.html) guide to insall and use the app. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](https://github.com/fbsamples/f8app/issues).\n* Look through the [open issues](https://github.com/fbsamples/f8app/issues). Provide workarounds, ask for clarification, or suggest labels.\n* If you find an issue you would like to fix, add a comment stating your intent to tackle it. Then open a pull request with your changes. Issues tagged as [_good first issue_](https://github.com/fbsamples/f8app/labels/good%20first%20issue) are a good place to get started.\n\n## Pull Request Workflow\n\n_Before_ submitting a pull request, please make sure the following is done…\n\n1. Fork the repo and create your branch from `master`. A guide on how to fork a repository: https://help.github.com/articles/fork-a-repo/\n\n   Open terminal (e.g. Terminal, iTerm, Git Bash or Git Shell) and type:\n\n   ```sh\n   git clone https://github.com/<your_username>/f8app\n   cd f8app\n   git checkout -b my_branch\n   ```\n\n   Note: Replace `<your_username>` with your GitHub username\n\n2. Follow the [setup instructions](http://makeitopen.com/docs/en/1-A1-local-setup.html) to run the app and test your changes.\n\n3. If you haven't already, complete the Contributor License Agreement (\"CLA\").\n\nAfter opening your pull request, ensure all tests pass on Circle CI. Pull requests that break tests are unlikely to be merged. If a test fails and you believe it is unrelated to your change, leave a comment on the pull request explaining why.\n\n## Contributor License Agreement (\"CLA\")\nIn order to accept your pull request, we need you to submit a CLA. You only need\nto do this once to work on any of Facebook's open source projects.\n\nComplete your CLA here: <https://code.facebook.com/cla>\n\n## Issues\nWe use GitHub issues to track public bugs. Please ensure your description is\nclear and has sufficient instructions to be able to reproduce the issue.\n\n### Security Bugs\n\nFacebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe\ndisclosure of security bugs. In those cases, please go through the process\noutlined on that page and do not file a public issue.\n\n## Code Conventions\n\n* ES6 syntax when possible.\n* Use [Flow types](http://flowtype.org/).\n* Avd abbr wrds.\n\n## License\nBy contributing to this repo, you agree that your contributions will be licensed\nunder its license.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2016 Facebook, Inc.\n\nYou are hereby granted a non-exclusive, worldwide, royalty-free license to\nuse, copy, modify, and distribute this software in source code or binary\nform for use in connection with the web services and APIs provided by\nFacebook.\n\nAs with any software that integrates with the Facebook platform, your use\nof this software is subject to the Facebook Developer Principles and\nPolicies [http://developers.facebook.com/policy/]. This copyright notice\nshall be included in all copies or substantial portions of the software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\nTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE\n"
  },
  {
    "path": "README.md",
    "content": "# F8 App 2017\n\nThis is the entire source code of the official [F8](https://fbf8.com/) app of 2017, available on [Google Play](https://play.google.com/store/apps/details?id=com.facebook.f8) and the [App Store](https://itunes.apple.com/us/app/f8/id853467066).\n\n<img src=\".github/screenshot-app@2x.png\" width=\"800\">\n\n\n## How We Build It\n\nWe've created a series of tutorials at http://makeitopen.com/ that explain how we built the app, and that dive into how we used React Native, Redux, Relay, GraphQL, and more.\n\nCheck out how to set the app up for local development [here](http://makeitopen.com/docs/en/1-A1-local-setup.html)!\n"
  },
  {
    "path": "__mocks__/react-native.js",
    "content": "import rn from \"react-native\";\n\njest.mock(\"Linking\", () => {\n  return {\n    addEventListener: jest.fn(),\n    removeEventListener: jest.fn(),\n    openURL: jest.fn(),\n    canOpenURL: jest.fn(),\n    getInitialURL: jest.fn()\n  };\n});\n\njest.mock(\"PushNotificationIOS\", () => ({\n  addEventListener: jest.fn(),\n  requestPermissions: jest.fn()\n}));\n\nmodule.exports = rn;\n"
  },
  {
    "path": "android/app/BUCK",
    "content": "import re\n\n# To learn about Buck see [Docs](https://buckbuild.com/).\n# To run your application with Buck:\n# - install Buck\n# - `npm start` - to start the packager\n# - `cd android`\n# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname \"CN=Android Debug,O=Android,C=US\"`\n# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck\n# - `buck install -r android/app` - compile, install and run application\n#\n\nlib_deps = []\nfor jarfile in glob(['libs/*.jar']):\n  name = 'jars__' + re.sub(r'^.*/([^/]+)\\.jar$', r'\\1', jarfile)\n  lib_deps.append(':' + name)\n  prebuilt_jar(\n    name = name,\n    binary_jar = jarfile,\n  )\n\nfor aarfile in glob(['libs/*.aar']):\n  name = 'aars__' + re.sub(r'^.*/([^/]+)\\.aar$', r'\\1', aarfile)\n  lib_deps.append(':' + name)\n  android_prebuilt_aar(\n    name = name,\n    aar = aarfile,\n  )\n\nandroid_library(\n  name = 'all-libs',\n  exported_deps = lib_deps\n)\n\nandroid_library(\n  name = 'app-code',\n  srcs = glob([\n    'src/main/java/**/*.java',\n  ]),\n  deps = [\n    ':all-libs',\n    ':build_config',\n    ':res',\n  ],\n)\n\nandroid_build_config(\n  name = 'build_config',\n  package = 'com.f82017',\n)\n\nandroid_resource(\n  name = 'res',\n  res = 'src/main/res',\n  package = 'com.f82017',\n)\n\nandroid_binary(\n  name = 'app',\n  package_type = 'debug',\n  manifest = 'src/main/AndroidManifest.xml',\n  keystore = '//android/keystores:debug',\n  deps = [\n    ':app-code',\n  ],\n)\n"
  },
  {
    "path": "android/app/build.gradle",
    "content": "apply plugin: \"com.android.application\"\n\nimport com.android.build.OutputFile\n\n/**\n * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets\n * and bundleReleaseJsAndAssets).\n * These basically call `react-native bundle` with the correct arguments during the Android build\n * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the\n * bundle directly from the development server. Below you can see all the possible configurations\n * and their defaults. If you decide to add a configuration block, make sure to add it before the\n * `apply from: \"../../node_modules/react-native/react.gradle\"` line.\n *\n * project.ext.react = [\n *   // the name of the generated asset file containing your JS bundle\n *   bundleAssetName: \"index.android.bundle\",\n *\n *   // the entry file for bundle generation\n *   entryFile: \"index.android.js\",\n *\n *   // whether to bundle JS and assets in debug mode\n *   bundleInDebug: false,\n *\n *   // whether to bundle JS and assets in release mode\n *   bundleInRelease: true,\n *\n *   // whether to bundle JS and assets in another build variant (if configured).\n *   // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants\n *   // The configuration property can be in the following formats\n *   //         'bundleIn${productFlavor}${buildType}'\n *   //         'bundleIn${buildType}'\n *   // bundleInFreeDebug: true,\n *   // bundleInPaidRelease: true,\n *   // bundleInBeta: true,\n *\n *   // the root of your project, i.e. where \"package.json\" lives\n *   root: \"../../\",\n *\n *   // where to put the JS bundle asset in debug mode\n *   jsBundleDirDebug: \"$buildDir/intermediates/assets/debug\",\n *\n *   // where to put the JS bundle asset in release mode\n *   jsBundleDirRelease: \"$buildDir/intermediates/assets/release\",\n *\n *   // where to put drawable resources / React Native assets, e.g. the ones you use via\n *   // require('./image.png')), in debug mode\n *   resourcesDirDebug: \"$buildDir/intermediates/res/merged/debug\",\n *\n *   // where to put drawable resources / React Native assets, e.g. the ones you use via\n *   // require('./image.png')), in release mode\n *   resourcesDirRelease: \"$buildDir/intermediates/res/merged/release\",\n *\n *   // by default the gradle tasks are skipped if none of the JS files or assets change; this means\n *   // that we don't look at files in android/ or ios/ to determine whether the tasks are up to\n *   // date; if you have any other folders that you want to ignore for performance reasons (gradle\n *   // indexes the entire tree), add them here. Alternatively, if you have JS files in android/\n *   // for example, you might want to remove it from here.\n *   inputExcludes: [\"android/**\", \"ios/**\"],\n *\n *   // override which node gets called and with what additional arguments\n *   nodeExecutableAndArgs: [\"node\"]\n *\n *   // supply additional arguments to the packager\n *   extraPackagerArgs: []\n * ]\n */\n\napply from: \"../../node_modules/react-native/react.gradle\"\n\n/**\n * Set this to true to create two separate APKs instead of one:\n *   - An APK that only works on ARM devices\n *   - An APK that only works on x86 devices\n * The advantage is the size of the APK is reduced by about 4MB.\n * Upload all the APKs to the Play Store and people will download\n * the correct one based on the CPU architecture of their device.\n */\ndef enableSeparateBuildPerCPUArchitecture = false\n\n/**\n * Run Proguard to shrink the Java bytecode in release builds.\n */\ndef enableProguardInReleaseBuilds = false\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.1\"\n\n    defaultConfig {\n        applicationId \"com.facebook.f8\"\n        minSdkVersion 16\n        targetSdkVersion 23\n        versionCode 10000410\n        versionName \"4.0.0\"\n        ndk {\n            abiFilters \"armeabi-v7a\", \"x86\"\n        }\n    }\n    signingConfigs {\n        release {\n            storeFile file(F8_RELEASE_STORE_FILE)\n            storePassword F8_RELEASE_STORE_PASSWORD\n            keyAlias F8_RELEASE_KEY_ALIAS\n            keyPassword F8_RELEASE_KEY_PASSWORD\n        }\n    }\n    splits {\n        abi {\n            reset()\n            enable enableSeparateBuildPerCPUArchitecture\n            universalApk false  // If true, also generate a universal APK\n            include \"armeabi-v7a\", \"x86\"\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled enableProguardInReleaseBuilds\n            proguardFiles getDefaultProguardFile(\"proguard-android.txt\"), \"proguard-rules.pro\"\n            signingConfig signingConfigs.release\n            debuggable false\n        }\n    }\n    // applicationVariants are e.g. debug, release\n    applicationVariants.all { variant ->\n        variant.outputs.each { output ->\n            // For each separate APK per architecture, set a unique version code as described here:\n            // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits\n            def versionCodes = [\"armeabi-v7a\":1, \"x86\":2]\n            def abi = output.getFilter(OutputFile.ABI)\n            if (abi != null) {  // null for the universal-debug, universal-release variants\n                output.versionCodeOverride =\n                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode\n            }\n        }\n    }\n}\n\ndependencies {\n    compile project(':react-native-linear-gradient')\n    compile project(':react-native-native-video-player')\n    compile project(':react-native-push-notification')\n    compile project(':react-native-photo-view')\n    compile ('com.google.android.gms:play-services-gcm:10.0.1')\n    // See https://stackoverflow.com/questions/44190829/facebook-sdk-android-error-building/44190896#44190896\n    compile(project(':react-native-fbsdk')){\n      exclude(group: 'com.facebook.android', module: 'facebook-android-sdk')\n    }\n    compile \"com.facebook.android:facebook-android-sdk:4.22.1\"\n    compile fileTree(dir: \"libs\", include: [\"*.jar\"])\n    compile \"com.android.support:appcompat-v7:23.0.1\"\n    compile \"com.facebook.react:react-native:+\"  // From node_modules\n}\n\n// Run this once to be able to run the application with BUCK\n// puts all compile dependencies into folder libs for BUCK to use\ntask copyDownloadableDepsToLibs(type: Copy) {\n    from configurations.compile\n    into 'libs'\n}\n"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Disabling obfuscation is useful if you collect stack traces from production crashes\n# (unless you are using a system that supports de-obfuscate the stack traces).\n-dontobfuscate\n\n# React Native\n\n# Keep our interfaces so they can be used by other ProGuard rules.\n# See http://sourceforge.net/p/proguard/bugs/466/\n-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip\n-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters\n-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip\n\n# Do not strip any method/class that is annotated with @DoNotStrip\n-keep @com.facebook.proguard.annotations.DoNotStrip class *\n-keep @com.facebook.common.internal.DoNotStrip class *\n-keepclassmembers class * {\n    @com.facebook.proguard.annotations.DoNotStrip *;\n    @com.facebook.common.internal.DoNotStrip *;\n}\n\n-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {\n  void set*(***);\n  *** get*();\n}\n\n-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }\n-keep class * extends com.facebook.react.bridge.NativeModule { *; }\n-keepclassmembers,includedescriptorclasses class * { native <methods>; }\n-keepclassmembers class *  { @com.facebook.react.uimanager.UIProp <fields>; }\n-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }\n-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }\n\n-dontwarn com.facebook.react.**\n\n# okhttp\n\n-keepattributes Signature\n-keepattributes *Annotation*\n-keep class okhttp3.** { *; }\n-keep interface okhttp3.** { *; }\n-dontwarn okhttp3.**\n\n# okio\n\n-keep class sun.misc.Unsafe { *; }\n-dontwarn java.nio.file.*\n-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement\n-dontwarn okio.**\n"
  },
  {
    "path": "android/app/src/debug/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">F8 Debug</string>\n    <string name=\"facebook_app_id\">619048868222429</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/debug/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.ReactNative.AppCompat.Light\">\n        <!-- Customize your theme here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.facebook.f8\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <permission\n        android:name=\"${applicationId}.permission.C2D_MESSAGE\"\n        android:protectionLevel=\"signature\" />\n    <uses-permission android:name=\"${applicationId}.permission.C2D_MESSAGE\" />\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"23\" />\n\n    <application\n      android:name=\".MainApplication\"\n      android:allowBackup=\"false\"\n        tools:replace=\"android:allowBackup\"\n      android:label=\"@string/app_name\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:theme=\"@style/AppTheme\">\n      <activity\n        android:name=\".MainActivity\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\"\n          android:exported=\"true\"\n        android:configChanges=\"keyboard|keyboardHidden|orientation|screenSize\"\n        android:windowSoftInputMode=\"adjustResize\">\n        <intent-filter>\n            <action android:name=\"android.intent.action.MAIN\" />\n            <category android:name=\"android.intent.category.LAUNCHER\" />\n        </intent-filter>\n      </activity>\n        <meta-data android:name=\"com.facebook.sdk.ApplicationId\" android:value=\"@string/facebook_app_id\"/>\n        <activity\n            tools:replace=\"android:theme\"\n            android:name=\"com.facebook.FacebookActivity\"\n            android:exported=\"false\"\n            android:configChanges=\"keyboard|keyboardHidden|screenLayout|screenSize|orientation\"\n            android:label=\"@string/app_name\"\n            android:theme=\"@android:style/Theme.Translucent.NoTitleBar\"/>\n        <receiver\n            android:name=\"com.google.android.gms.gcm.GcmReceiver\"\n            android:exported=\"true\"\n            android:permission=\"com.google.android.c2dm.permission.SEND\" >\n            <intent-filter>\n                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n                <category android:name=\"${applicationId}\" />\n            </intent-filter>\n        </receiver>\n\n        <receiver android:exported=\"false\" android:name=\"com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher\" />\n        <receiver android:exported=\"true\" android:name=\"com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.BOOT_COMPLETED\" />\n            </intent-filter>\n        </receiver>\n        <service android:exported=\"false\" android:name=\"com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService\"/>\n        <service\n            android:name=\"com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService\"\n            android:exported=\"false\" >\n            <intent-filter>\n                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n            </intent-filter>\n        </service>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/java/com/facebook/f8/MainActivity.java",
    "content": "package com.facebook.f8;\n\nimport com.facebook.react.ReactActivity;\nimport android.content.Intent;\n/*import com.facebook.reactnative.androidsdk.FBSDKPackage;\nimport com.microsoft.codepush.react.CodePush;*/\n\npublic class MainActivity extends ReactActivity {\n\n    /**\n     * Returns the name of the main component registered from JavaScript.\n     * This is used to schedule rendering of the component.\n     */\n    @Override\n    protected String getMainComponentName() {\n        return \"F82017\";\n    }\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data);\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/java/com/facebook/f8/MainApplication.java",
    "content": "package com.facebook.f8;\n\nimport android.app.Application;\n\nimport com.facebook.react.ReactApplication;\nimport com.BV.LinearGradient.LinearGradientPackage;\nimport com.wog.videoplayer.VideoPlayerPackage;\nimport com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;\nimport com.reactnative.photoview.PhotoViewPackage;\nimport com.facebook.react.ReactNativeHost;\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.shell.MainReactPackage;\nimport com.facebook.soloader.SoLoader;\n\nimport com.facebook.CallbackManager;\nimport com.facebook.FacebookSdk;\nimport com.facebook.reactnative.androidsdk.FBSDKPackage;\nimport com.facebook.appevents.AppEventsLogger;\n\n\nimport java.util.Arrays;\nimport java.util.List;\n\n\n\npublic class MainApplication extends Application implements ReactApplication {\n\n  private static CallbackManager mCallbackManager = CallbackManager.Factory.create();\n\n  protected static CallbackManager getCallbackManager() {\n    return mCallbackManager;\n  }\n\n  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {\n\n    @Override\n    public boolean getUseDeveloperSupport() {\n      return BuildConfig.DEBUG;\n    }\n\n    @Override\n    protected List<ReactPackage> getPackages() {\n      return Arrays.<ReactPackage>asList(\n        new MainReactPackage(),\n            new VideoPlayerPackage(),\n            new PhotoViewPackage(),\n        new FBSDKPackage(mCallbackManager),\n        new ReactNativePushNotificationPackage(),\n        new LinearGradientPackage()\n      );\n    }\n  };\n\n  @Override\n  public ReactNativeHost getReactNativeHost() {\n    return mReactNativeHost;\n  }\n\n  @Override\n  public void onCreate() {\n    super.onCreate();\n    SoLoader.init(this, /* native exopackage */ false);\n    FacebookSdk.sdkInitialize(getApplicationContext());\n    AppEventsLogger.activateApp(this);\n  }\n}\n"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">F8</string>\n    <string name=\"facebook_app_id\">619048868222429</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.ReactNative.AppCompat.Light\">\n        <!-- Customize your theme here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.0.1'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        mavenLocal()\n        jcenter()\n        google()\n        maven {\n            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm\n            url \"$rootDir/../node_modules/react-native/android\"\n        }\n    }\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Jan 17 15:39:05 EST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.2-all.zip\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\nandroid.useDeprecatedNdk=true\n\nF8_RELEASE_STORE_FILE=f8-release.keystore\nF8_RELEASE_KEY_ALIAS=f8-release\nF8_RELEASE_STORE_PASSWORD=K0IvEbWKWyrZR3KpbfDi\nF8_RELEASE_KEY_PASSWORD=D7UhkezSHJqXbBVlAlxA"
  },
  {
    "path": "android/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "android/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "android/keystores/BUCK",
    "content": "keystore(\n  name = 'debug',\n  store = 'debug.keystore',\n  properties = 'debug.keystore.properties',\n  visibility = [\n    'PUBLIC',\n  ],\n)\n"
  },
  {
    "path": "android/keystores/debug.keystore.properties",
    "content": "key.store=debug.keystore\nkey.alias=androiddebugkey\nkey.store.password=android\nkey.alias.password=android\n"
  },
  {
    "path": "android/settings.gradle",
    "content": "rootProject.name = 'F82017'\ninclude ':react-native-linear-gradient'\nproject(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')\ninclude ':react-native-native-video-player'\nproject(':react-native-native-video-player').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-native-video-player/android')\ninclude ':react-native-push-notification'\nproject(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android')\ninclude ':react-native-photo-view'\nproject(':react-native-photo-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-photo-view/android')\n\ninclude ':app'\ninclude ':react-native-fbsdk'\nproject(':react-native-fbsdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fbsdk/android')\n"
  },
  {
    "path": "app.json",
    "content": "{\n  \"name\": \"F82017\",\n  \"displayName\": \"F82017\"\n}"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3\"\nservices:\n  mongo:\n    image: mongo\n    networks:\n      default:\n        aliases:\n          - mongo\n  mongorestore:\n    build: ./server/mongorestore\n    depends_on:\n      - mongo\n  parse:\n    build: ./server/parse-server\n    ports:\n      - \"1337:1337\"\n    depends_on:\n      - mongo\n  parse_dashboard:\n    build: ./server/parse-dashboard\n    ports:\n      - \"4040:4040\"\n  graphql:\n    build: ./server/graphql\n    environment:\n      - PARSE_URL=http://parse:1337/parse\n    ports:\n      - \"4000:4000\"\n"
  },
  {
    "path": "index.android.js",
    "content": "import { AppRegistry } from \"react-native\";\nimport setup from \"./js/setup\";\n\nAppRegistry.registerComponent(\"F82017\", setup);\n"
  },
  {
    "path": "index.ios.js",
    "content": "import { AppRegistry } from \"react-native\";\nimport setup from \"./js/setup\";\n\nAppRegistry.registerComponent(\"F82017\", setup);\n"
  },
  {
    "path": "ios/F82017/AppDelegate.h",
    "content": "/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : UIResponder <UIApplicationDelegate>\n\n@property (nonatomic, strong) UIWindow *window;\n\n@end\n"
  },
  {
    "path": "ios/F82017/AppDelegate.m",
    "content": "/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#import \"AppDelegate.h\"\n\n#import <React/RCTBundleURLProvider.h>\n#import <React/RCTRootView.h>\n\n#import <FBSDKCoreKit/FBSDKCoreKit.h>\n#import <React/RCTPushNotificationManager.h>\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions\n{\n  NSURL *jsCodeLocation;\n\n  [[FBSDKApplicationDelegate sharedInstance] application:application\n                           didFinishLaunchingWithOptions:launchOptions];\n  \n#ifdef DEBUG\n    jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\"index.ios\" fallbackResource:nil];\n#else\n    jsCodeLocation = [[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"];\n#endif\n\n  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation\n                                                      moduleName:@\"F82017\"\n                                               initialProperties:nil\n                                                   launchOptions:launchOptions];\n  rootView.backgroundColor = [[UIColor alloc] initWithRed:0.98f green:0.98f blue:0.94f alpha:1];\n  \n  NSArray *objects = [[NSBundle mainBundle] loadNibNamed:@\"LaunchScreen\" owner:self options:nil];\n  UIImageView *loadingView = [[[objects objectAtIndex:0] subviews] objectAtIndex:0];\n  loadingView = [[UIImageView alloc] initWithImage:[loadingView image]];\n  loadingView.frame = [UIScreen mainScreen].bounds;\n  loadingView.contentMode = UIViewContentModeScaleAspectFill;\n  \n  rootView.loadingView = loadingView;\n\n  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];\n  UIViewController *rootViewController = [UIViewController new];\n  rootViewController.view = rootView;\n  self.window.rootViewController = rootViewController;\n  [self.window makeKeyAndVisible];\n  return YES;\n}\n\n\n- (BOOL)application:(UIApplication *)application\n            openURL:(NSURL *)url\n            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {\n  \n  BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:application\n                                                                openURL:url\n                                                      sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]\n                                                             annotation:options[UIApplicationOpenURLOptionsAnnotationKey]\n                  ];\n  // Add any custom logic here.\n  return handled;\n}\n\n- (void)applicationDidBecomeActive:(UIApplication *)application {\n  [FBSDKAppEvents activateApp];\n}\n\n// Required to register for notifications\n- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings\n{\n  [RCTPushNotificationManager didRegisterUserNotificationSettings:notificationSettings];\n}\n// Required for the register event.\n- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken\n{\n  [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];\n}\n// Required for the notification event. You must call the completion handler after handling the remote notification.\n- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo\nfetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {\n  {\n    [RCTPushNotificationManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];\n  }\n}\n\n// Optionally implement this method over the previous to receive remote notifications. However\n// implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible.\n// If your delegate implements both methods, the app object calls the `application:didReceiveRemoteNotification:fetchCompletionHandler:` method\n// Either this method or `application:didReceiveRemoteNotification:fetchCompletionHandler:` is required in order to receive remote notifications.\n//\n// Required for the registrationError event.\n- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error\n{\n  [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error];\n}\n// Required for the notification event.\n- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification\n{\n  [RCTPushNotificationManager didReceiveRemoteNotification:notification];\n}\n// Required for the localNotification event.\n- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification\n{\n  [RCTPushNotificationManager didReceiveLocalNotification:notification];\n}\n\n@end\n"
  },
  {
    "path": "ios/F82017/Base.lproj/LaunchScreen.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVersion=\"11762\" systemVersion=\"16B2555\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\">\n    <device id=\"retina5_5\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"11757\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <placeholder placeholderIdentifier=\"IBFilesOwner\" id=\"-1\" userLabel=\"File's Owner\"/>\n        <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"-2\" customClass=\"UIResponder\"/>\n        <view contentMode=\"scaleToFill\" id=\"iN0-l3-epB\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"480\"/>\n            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n            <subviews>\n                <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"launchscreen.png\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"UII-PU-0QB\">\n                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"480\"/>\n                </imageView>\n            </subviews>\n            <color key=\"backgroundColor\" red=\"0.98431372549019602\" green=\"0.97647058823529409\" blue=\"0.94117647058823528\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n            <constraints>\n                <constraint firstAttribute=\"bottom\" secondItem=\"UII-PU-0QB\" secondAttribute=\"bottom\" id=\"0Xg-Nz-jcU\"/>\n                <constraint firstItem=\"UII-PU-0QB\" firstAttribute=\"top\" secondItem=\"iN0-l3-epB\" secondAttribute=\"top\" id=\"0em-D4-i81\"/>\n                <constraint firstItem=\"UII-PU-0QB\" firstAttribute=\"leading\" secondItem=\"iN0-l3-epB\" secondAttribute=\"leading\" id=\"HFX-bM-GDN\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"UII-PU-0QB\" secondAttribute=\"trailing\" id=\"aiP-dg-HHa\"/>\n            </constraints>\n            <nil key=\"simulatedStatusBarMetrics\"/>\n            <freeformSimulatedSizeMetrics key=\"simulatedDestinationMetrics\"/>\n            <point key=\"canvasLocation\" x=\"590\" y=\"490\"/>\n        </view>\n    </objects>\n    <resources>\n        <image name=\"launchscreen.png\" width=\"667\" height=\"667\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios/F82017/F82017.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>aps-environment</key>\n\t<string>development</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/F82017/Images.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"size\" : \"1024x1024\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "ios/F82017/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>F8</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>4.0.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>fb619048868222429</string>\n\t\t\t\t<string>f8</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>410</string>\n\t<key>FacebookAppID</key>\n\t<string>619048868222429</string>\n\t<key>FacebookDisplayName</key>\n\t<string>F8 Developer Conference 2017</string>\n\t<key>ITSAppUsesNonExemptEncryption</key>\n\t<false/>\n\t<key>LSApplicationQueriesSchemes</key>\n\t<array>\n\t\t<string>fbapi</string>\n\t\t<string>fb-messenger-api</string>\n\t\t<string>fbauth2</string>\n\t\t<string>fbshareextension</string>\n\t</array>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSExceptionDomains</key>\n\t\t<dict>\n\t\t\t<key>localhost</key>\n\t\t\t<dict>\n\t\t\t\t<key>NSExceptionAllowsInsecureHTTPLoads</key>\n\t\t\t\t<true/>\n\t\t\t</dict>\n\t\t</dict>\n\t\t<key>NSAllowsArbitraryLoads</key>\n\t\t<false/>\n\t</dict>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string></string>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>Required for use with the FacebookSDK</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/F82017/main.m",
    "content": "/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#import <UIKit/UIKit.h>\n\n#import \"AppDelegate.h\"\n\nint main(int argc, char * argv[]) {\n  @autoreleasepool {\n    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n  }\n}\n"
  },
  {
    "path": "ios/F82017-tvOS/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string></string>\n\t<key>NSAppTransportSecurity</key>\n\t<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->\n\t<dict>\n\t\t<key>NSExceptionDomains</key>\n\t\t<dict>\n\t\t\t<key>localhost</key>\n\t\t\t<dict>\n\t\t\t\t<key>NSExceptionAllowsInsecureHTTPLoads</key>\n\t\t\t\t<true/>\n\t\t\t</dict>\n\t\t</dict>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/F82017-tvOSTests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/F82017.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };\n\t\t00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };\n\t\t00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };\n\t\t00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };\n\t\t00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };\n\t\t00E356F31AD99517003FC87E /* F82017Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* F82017Tests.m */; };\n\t\t0186555E1614488AAE4A6B43 /* libRNVideoPlayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */; };\n\t\t133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };\n\t\t139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };\n\t\t139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };\n\t\t13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };\n\t\t13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };\n\t\t13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };\n\t\t13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };\n\t\t140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };\n\t\t146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };\n\t\t2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };\n\t\t2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };\n\t\t2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };\n\t\t2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; };\n\t\t2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; };\n\t\t2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; };\n\t\t2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; };\n\t\t2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; };\n\t\t2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; };\n\t\t2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; };\n\t\t2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; };\n\t\t2DCD954D1E0B4F2C00145EB5 /* F82017Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* F82017Tests.m */; };\n\t\t3009A9271DF88122006B5D62 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9231DF88122006B5D62 /* Bolts.framework */; };\n\t\t3009A9281DF88122006B5D62 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */; };\n\t\t3009A9291DF88122006B5D62 /* FBSDKLoginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */; };\n\t\t3009A92A1DF88122006B5D62 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */; };\n\t\t3035251A1E4BFD4E0003A60D /* launchscreen.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525171E4BFD4E0003A60D /* launchscreen.png */; };\n\t\t3035251B1E4BFD4E0003A60D /* launchscreen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525181E4BFD4E0003A60D /* launchscreen@2x.png */; };\n\t\t3035251C1E4BFD4E0003A60D /* launchscreen@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525191E4BFD4E0003A60D /* launchscreen@3x.png */; };\n\t\t308E35A01E8C2A5E00CAC09E /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */; };\n\t\t309371D11E2DBE160019E3C2 /* 512.png in Resources */ = {isa = PBXBuildFile; fileRef = 309371D01E2DBE160019E3C2 /* 512.png */; };\n\t\t309371D31E2DBEAE0019E3C2 /* 512@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 309371D21E2DBEAE0019E3C2 /* 512@2x.png */; };\n\t\t3098A8FF1E7C64CD0003742F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3098A8FE1E7C64CD0003742F /* libz.tbd */; };\n\t\t5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };\n\t\t832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };\n\t\tB2C3247ED20945EDAAD12F70 /* libRCTFBSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E12A673783B9486B87E8AE7B /* libRCTFBSDK.a */; };\n\t\tFC554BF677604211A694CB54 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTActionSheet;\n\t\t};\n\t\t00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTGeolocation;\n\t\t};\n\t\t00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 58B5115D1A9E6B3D00147676;\n\t\t\tremoteInfo = RCTImage;\n\t\t};\n\t\t00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 58B511DB1A9E6C8500147676;\n\t\t\tremoteInfo = RCTNetwork;\n\t\t};\n\t\t00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 832C81801AAF6DEF007FA2F7;\n\t\t\tremoteInfo = RCTVibration;\n\t\t};\n\t\t00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 13B07F861A680F5B00A75B9A;\n\t\t\tremoteInfo = F82017;\n\t\t};\n\t\t139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTSettings;\n\t\t};\n\t\t139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3C86DF461ADF2C930047B81A;\n\t\t\tremoteInfo = RCTWebSocket;\n\t\t};\n\t\t146834031AC3E56700842450 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;\n\t\t\tremoteInfo = React;\n\t\t};\n\t\t2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;\n\t\t\tremoteInfo = \"F82017-tvOS\";\n\t\t};\n\t\t308E335D1E8C1E0800CAC09E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = AC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 9350E0F11CE3B0920041D815;\n\t\t\tremoteInfo = RCTFBSDK;\n\t\t};\n\t\t308E35411E8C280700CAC09E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = D88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 84BF382F1DBF52980044F03A;\n\t\t\tremoteInfo = RNVideoPlayer;\n\t\t};\n\t\t308E359C1E8C2A3D00CAC09E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTPushNotification;\n\t\t};\n\t\t308E359E1E8C2A3D00CAC09E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D05745F1DE6004600184BB4;\n\t\t\tremoteInfo = \"RCTPushNotification-tvOS\";\n\t\t};\n\t\t308E376E1E8D63B000CAC09E /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = BVLinearGradient;\n\t\t};\n\t\t3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A283A1D9B042B00D4039D;\n\t\t\tremoteInfo = \"RCTImage-tvOS\";\n\t\t};\n\t\t3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28471D9B043800D4039D;\n\t\t\tremoteInfo = \"RCTLinking-tvOS\";\n\t\t};\n\t\t3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28541D9B044C00D4039D;\n\t\t\tremoteInfo = \"RCTNetwork-tvOS\";\n\t\t};\n\t\t3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28611D9B046600D4039D;\n\t\t\tremoteInfo = \"RCTSettings-tvOS\";\n\t\t};\n\t\t3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A287B1D9B048500D4039D;\n\t\t\tremoteInfo = \"RCTText-tvOS\";\n\t\t};\n\t\t3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28881D9B049200D4039D;\n\t\t\tremoteInfo = \"RCTWebSocket-tvOS\";\n\t\t};\n\t\t3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28131D9B038B00D4039D;\n\t\t\tremoteInfo = \"React-tvOS\";\n\t\t};\n\t\t3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3C059A1DE3340900C268FA;\n\t\t\tremoteInfo = yoga;\n\t\t};\n\t\t3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3C06751DE3340C00C268FA;\n\t\t\tremoteInfo = \"yoga-tvOS\";\n\t\t};\n\t\t3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;\n\t\t\tremoteInfo = cxxreact;\n\t\t};\n\t\t3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;\n\t\t\tremoteInfo = \"cxxreact-tvOS\";\n\t\t};\n\t\t3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4;\n\t\t\tremoteInfo = jschelpers;\n\t\t};\n\t\t3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 3D3CD9181DE5FBD800167DC4;\n\t\t\tremoteInfo = \"jschelpers-tvOS\";\n\t\t};\n\t\t5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTAnimation;\n\t\t};\n\t\t5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 2D2A28201D9B03D100D4039D;\n\t\t\tremoteInfo = \"RCTAnimation-tvOS\";\n\t\t};\n\t\t78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 134814201AA4EA6300B7C361;\n\t\t\tremoteInfo = RCTLinking;\n\t\t};\n\t\t832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = 58B5119B1A9E6C1200147676;\n\t\t\tremoteInfo = RCTText;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = \"<group>\"; };\n\t\t00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTActionSheet.xcodeproj; path = \"../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTGeolocation.xcodeproj; path = \"../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTImage.xcodeproj; path = \"../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTNetwork.xcodeproj; path = \"../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTVibration.xcodeproj; path = \"../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t00E356EE1AD99517003FC87E /* F82017Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = F82017Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t00E356F21AD99517003FC87E /* F82017Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = F82017Tests.m; sourceTree = \"<group>\"; };\n\t\t0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVideoPlayer.a; sourceTree = \"<group>\"; };\n\t\t139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTSettings.xcodeproj; path = \"../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTWebSocket.xcodeproj; path = \"../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t13B07F961A680F5B00A75B9A /* F82017.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = F82017.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = F82017/AppDelegate.h; sourceTree = \"<group>\"; };\n\t\t13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = F82017/AppDelegate.m; sourceTree = \"<group>\"; };\n\t\t13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = \"<group>\"; };\n\t\t13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = F82017/Images.xcassets; sourceTree = \"<group>\"; };\n\t\t13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = F82017/Info.plist; sourceTree = \"<group>\"; };\n\t\t13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = F82017/main.m; sourceTree = \"<group>\"; };\n\t\t146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = React.xcodeproj; path = \"../node_modules/react-native/React/React.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"F82017-tvOS.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = \"F82017-tvOSTests.xctest\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3009A9231DF88122006B5D62 /* Bolts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bolts.framework; path = ../../../../../../Documents/FacebookSDK/Bolts.framework; sourceTree = \"<group>\"; };\n\t\t3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKCoreKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKCoreKit.framework; sourceTree = \"<group>\"; };\n\t\t3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKLoginKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKLoginKit.framework; sourceTree = \"<group>\"; };\n\t\t3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKShareKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKShareKit.framework; sourceTree = \"<group>\"; };\n\t\t303525171E4BFD4E0003A60D /* launchscreen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = launchscreen.png; sourceTree = \"<group>\"; };\n\t\t303525181E4BFD4E0003A60D /* launchscreen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = \"launchscreen@2x.png\"; sourceTree = \"<group>\"; };\n\t\t303525191E4BFD4E0003A60D /* launchscreen@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = \"launchscreen@3x.png\"; sourceTree = \"<group>\"; };\n\t\t30391C631E48DF35005B8B86 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };\n\t\t30391C651E48DF3F005B8B86 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };\n\t\t30391C671E48DF4D005B8B86 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };\n\t\t307A4B9C1E1D991A0062A662 /* F82017.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = F82017.entitlements; path = F82017/F82017.entitlements; sourceTree = \"<group>\"; };\n\t\t308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTPushNotification.xcodeproj; path = \"../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t309371D01E2DBE160019E3C2 /* 512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 512.png; sourceTree = \"<group>\"; };\n\t\t309371D21E2DBEAE0019E3C2 /* 512@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = \"512@2x.png\"; sourceTree = \"<group>\"; };\n\t\t3098A8FE1E7C64CD0003742F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };\n\t\t5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTAnimation.xcodeproj; path = \"../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = \"wrapper.pb-project\"; name = BVLinearGradient.xcodeproj; path = \"../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTLinking.xcodeproj; path = \"../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = RCTText.xcodeproj; path = \"../node_modules/react-native/Libraries/Text/RCTText.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\t8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = \"<group>\"; };\n\t\tAC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = \"wrapper.pb-project\"; name = RCTFBSDK.xcodeproj; path = \"../node_modules/react-native-fbsdk/ios/RCTFBSDK.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\tD88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = \"wrapper.pb-project\"; name = RNVideoPlayer.xcodeproj; path = \"../node_modules/react-native-native-video-player/ios/RNVideoPlayer.xcodeproj\"; sourceTree = \"<group>\"; };\n\t\tE12A673783B9486B87E8AE7B /* libRCTFBSDK.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTFBSDK.a; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t00E356EB1AD99517003FC87E /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t13B07F8C1A680F5B00A75B9A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t308E35A01E8C2A5E00CAC09E /* libRCTPushNotification.a in Frameworks */,\n\t\t\t\t3009A9291DF88122006B5D62 /* FBSDKLoginKit.framework in Frameworks */,\n\t\t\t\t3009A9271DF88122006B5D62 /* Bolts.framework in Frameworks */,\n\t\t\t\t5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,\n\t\t\t\t146834051AC3E58100842450 /* libReact.a in Frameworks */,\n\t\t\t\t5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,\n\t\t\t\t00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,\n\t\t\t\t00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,\n\t\t\t\t00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,\n\t\t\t\t133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,\n\t\t\t\t00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,\n\t\t\t\t139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,\n\t\t\t\t832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,\n\t\t\t\t00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,\n\t\t\t\t139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,\n\t\t\t\t3009A92A1DF88122006B5D62 /* FBSDKShareKit.framework in Frameworks */,\n\t\t\t\t3009A9281DF88122006B5D62 /* FBSDKCoreKit.framework in Frameworks */,\n\t\t\t\tB2C3247ED20945EDAAD12F70 /* libRCTFBSDK.a in Frameworks */,\n\t\t\t\tFC554BF677604211A694CB54 /* libBVLinearGradient.a in Frameworks */,\n\t\t\t\t0186555E1614488AAE4A6B43 /* libRNVideoPlayer.a in Frameworks */,\n\t\t\t\t3098A8FF1E7C64CD0003742F /* libz.tbd in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E4781E0B4A5D006451C7 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */,\n\t\t\t\t2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */,\n\t\t\t\t2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */,\n\t\t\t\t2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */,\n\t\t\t\t2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */,\n\t\t\t\t2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */,\n\t\t\t\t2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */,\n\t\t\t\t2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t00C302A81ABCB8CE00DB3ED1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00C302B61ABCB90400DB3ED1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00C302BC1ABCB91800DB3ED1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,\n\t\t\t\t3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00C302D41ABCB9D200DB3ED1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,\n\t\t\t\t3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00C302E01ABCB9EE00DB3ED1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00E356EF1AD99517003FC87E /* F82017Tests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00E356F21AD99517003FC87E /* F82017Tests.m */,\n\t\t\t\t00E356F01AD99517003FC87E /* Supporting Files */,\n\t\t\t);\n\t\t\tpath = F82017Tests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t00E356F01AD99517003FC87E /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t00E356F11AD99517003FC87E /* Info.plist */,\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t139105B71AF99BAD00B5F7CC /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,\n\t\t\t\t3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t139FDEE71B06529A00C62182 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,\n\t\t\t\t3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t13B07FAE1A68108700A75B9A /* F82017 */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t307A4B9C1E1D991A0062A662 /* F82017.entitlements */,\n\t\t\t\t008F07F21AC5B25A0029DE68 /* main.jsbundle */,\n\t\t\t\t13B07FAF1A68108700A75B9A /* AppDelegate.h */,\n\t\t\t\t13B07FB01A68108700A75B9A /* AppDelegate.m */,\n\t\t\t\t13B07FB51A68108700A75B9A /* Images.xcassets */,\n\t\t\t\t13B07FB61A68108700A75B9A /* Info.plist */,\n\t\t\t\t13B07FB11A68108700A75B9A /* LaunchScreen.xib */,\n\t\t\t\t13B07FB71A68108700A75B9A /* main.m */,\n\t\t\t);\n\t\t\tname = F82017;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t146834001AC3E56700842450 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t146834041AC3E56700842450 /* libReact.a */,\n\t\t\t\t3DAD3EA31DF850E9000B6D8A /* libReact.a */,\n\t\t\t\t3DAD3EA51DF850E9000B6D8A /* libyoga.a */,\n\t\t\t\t3DAD3EA71DF850E9000B6D8A /* libyoga.a */,\n\t\t\t\t3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */,\n\t\t\t\t3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,\n\t\t\t\t3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,\n\t\t\t\t3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t3006989C1F0338030070FAAA /* Recovered References */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tE12A673783B9486B87E8AE7B /* libRCTFBSDK.a */,\n\t\t\t\t8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */,\n\t\t\t\t0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */,\n\t\t\t\t309371D21E2DBEAE0019E3C2 /* 512@2x.png */,\n\t\t\t\t303525171E4BFD4E0003A60D /* launchscreen.png */,\n\t\t\t\t303525181E4BFD4E0003A60D /* launchscreen@2x.png */,\n\t\t\t\t309371D01E2DBE160019E3C2 /* 512.png */,\n\t\t\t\t303525191E4BFD4E0003A60D /* launchscreen@3x.png */,\n\t\t\t);\n\t\t\tname = \"Recovered References\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t308E33481E8C1E0800CAC09E /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t308E335E1E8C1E0800CAC09E /* libRCTFBSDK.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t308E353E1E8C280700CAC09E /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t308E35421E8C280700CAC09E /* libRNVideoPlayer.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t308E35981E8C2A3D00CAC09E /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */,\n\t\t\t\t308E359F1E8C2A3D00CAC09E /* libRCTPushNotification-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t308E376B1E8D63B000CAC09E /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t308E376F1E8D63B000CAC09E /* libBVLinearGradient.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t5E91572E1DD0AC6500FF2AA8 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */,\n\t\t\t\t5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t78C398B11ACF4ADC00677621 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t78C398B91ACF4ADC00677621 /* libRCTLinking.a */,\n\t\t\t\t3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7A3404E71E26487D967C39EB /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3098A8FE1E7C64CD0003742F /* libz.tbd */,\n\t\t\t\t30391C671E48DF4D005B8B86 /* libsqlite3.0.tbd */,\n\t\t\t\t30391C651E48DF3F005B8B86 /* SystemConfiguration.framework */,\n\t\t\t\t30391C631E48DF35005B8B86 /* CoreData.framework */,\n\t\t\t\t3009A9231DF88122006B5D62 /* Bolts.framework */,\n\t\t\t\t3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */,\n\t\t\t\t3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */,\n\t\t\t\t3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t832341AE1AAA6A7D00B99B32 /* Libraries */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */,\n\t\t\t\t5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,\n\t\t\t\t146833FF1AC3E56700842450 /* React.xcodeproj */,\n\t\t\t\t00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,\n\t\t\t\t00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,\n\t\t\t\t00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,\n\t\t\t\t78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,\n\t\t\t\t00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,\n\t\t\t\t139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,\n\t\t\t\t832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,\n\t\t\t\t00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,\n\t\t\t\t139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,\n\t\t\t\tAC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */,\n\t\t\t\tD88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */,\n\t\t\t\t75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */,\n\t\t\t);\n\t\t\tname = Libraries;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t832341B11AAA6A8300B99B32 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t832341B51AAA6A8300B99B32 /* libRCTText.a */,\n\t\t\t\t3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t83CBB9F61A601CBA00E9B192 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t13B07FAE1A68108700A75B9A /* F82017 */,\n\t\t\t\t832341AE1AAA6A7D00B99B32 /* Libraries */,\n\t\t\t\t00E356EF1AD99517003FC87E /* F82017Tests */,\n\t\t\t\t83CBBA001A601CBA00E9B192 /* Products */,\n\t\t\t\t7A3404E71E26487D967C39EB /* Frameworks */,\n\t\t\t\tB6298A78CBB448B794708EE9 /* Resources */,\n\t\t\t\t3006989C1F0338030070FAAA /* Recovered References */,\n\t\t\t);\n\t\t\tindentWidth = 2;\n\t\t\tsourceTree = \"<group>\";\n\t\t\ttabWidth = 2;\n\t\t};\n\t\t83CBBA001A601CBA00E9B192 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t13B07F961A680F5B00A75B9A /* F82017.app */,\n\t\t\t\t00E356EE1AD99517003FC87E /* F82017Tests.xctest */,\n\t\t\t\t2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */,\n\t\t\t\t2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB6298A78CBB448B794708EE9 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t00E356ED1AD99517003FC87E /* F82017Tests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget \"F82017Tests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t00E356EA1AD99517003FC87E /* Sources */,\n\t\t\t\t00E356EB1AD99517003FC87E /* Frameworks */,\n\t\t\t\t00E356EC1AD99517003FC87E /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t00E356F51AD99517003FC87E /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = F82017Tests;\n\t\t\tproductName = F82017Tests;\n\t\t\tproductReference = 00E356EE1AD99517003FC87E /* F82017Tests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t13B07F861A680F5B00A75B9A /* F82017 */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget \"F82017\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t13B07F871A680F5B00A75B9A /* Sources */,\n\t\t\t\t13B07F8C1A680F5B00A75B9A /* Frameworks */,\n\t\t\t\t13B07F8E1A680F5B00A75B9A /* Resources */,\n\t\t\t\t00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = F82017;\n\t\t\tproductName = \"Hello World\";\n\t\t\tproductReference = 13B07F961A680F5B00A75B9A /* F82017.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget \"F82017-tvOS\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2D02E4771E0B4A5D006451C7 /* Sources */,\n\t\t\t\t2D02E4781E0B4A5D006451C7 /* Frameworks */,\n\t\t\t\t2D02E4791E0B4A5D006451C7 /* Resources */,\n\t\t\t\t2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"F82017-tvOS\";\n\t\t\tproductName = \"F82017-tvOS\";\n\t\t\tproductReference = 2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t2D02E48F1E0B4A5D006451C7 /* F82017-tvOSTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget \"F82017-tvOSTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2D02E48C1E0B4A5D006451C7 /* Sources */,\n\t\t\t\t2D02E48D1E0B4A5D006451C7 /* Frameworks */,\n\t\t\t\t2D02E48E1E0B4A5D006451C7 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = \"F82017-tvOSTests\";\n\t\t\tproductName = \"F82017-tvOSTests\";\n\t\t\tproductReference = 2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t83CBB9F71A601CBA00E9B192 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 610;\n\t\t\t\tORGANIZATIONNAME = Facebook;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t00E356ED1AD99517003FC87E = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 6.2;\n\t\t\t\t\t\tTestTargetID = 13B07F861A680F5B00A75B9A;\n\t\t\t\t\t};\n\t\t\t\t\t13B07F861A680F5B00A75B9A = {\n\t\t\t\t\t\tDevelopmentTeam = 9B8M3M76P9;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Push = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t2D02E47A1E0B4A5D006451C7 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 8.2.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\t2D02E48F1E0B4A5D006451C7 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 8.2.1;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tTestTargetID = 2D02E47A1E0B4A5D006451C7;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject \"F82017\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 83CBB9F61A601CBA00E9B192;\n\t\t\tproductRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 308E376B1E8D63B000CAC09E /* Products */;\n\t\t\t\t\tProjectRef = 75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;\n\t\t\t\t\tProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;\n\t\t\t\t\tProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 308E33481E8C1E0800CAC09E /* Products */;\n\t\t\t\t\tProjectRef = AC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;\n\t\t\t\t\tProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;\n\t\t\t\t\tProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 78C398B11ACF4ADC00677621 /* Products */;\n\t\t\t\t\tProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;\n\t\t\t\t\tProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 308E35981E8C2A3D00CAC09E /* Products */;\n\t\t\t\t\tProjectRef = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;\n\t\t\t\t\tProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 832341B11AAA6A8300B99B32 /* Products */;\n\t\t\t\t\tProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;\n\t\t\t\t\tProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 139FDEE71B06529A00C62182 /* Products */;\n\t\t\t\t\tProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 146834001AC3E56700842450 /* Products */;\n\t\t\t\t\tProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tProductGroup = 308E353E1E8C280700CAC09E /* Products */;\n\t\t\t\t\tProjectRef = D88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t13B07F861A680F5B00A75B9A /* F82017 */,\n\t\t\t\t00E356ED1AD99517003FC87E /* F82017Tests */,\n\t\t\t\t2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */,\n\t\t\t\t2D02E48F1E0B4A5D006451C7 /* F82017-tvOSTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\t00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTActionSheet.a;\n\t\t\tremoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTGeolocation.a;\n\t\t\tremoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTImage.a;\n\t\t\tremoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTNetwork.a;\n\t\t\tremoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTVibration.a;\n\t\t\tremoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTSettings.a;\n\t\t\tremoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTWebSocket.a;\n\t\t\tremoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t146834041AC3E56700842450 /* libReact.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libReact.a;\n\t\t\tremoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t308E335E1E8C1E0800CAC09E /* libRCTFBSDK.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTFBSDK.a;\n\t\t\tremoteRef = 308E335D1E8C1E0800CAC09E /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t308E35421E8C280700CAC09E /* libRNVideoPlayer.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRNVideoPlayer.a;\n\t\t\tremoteRef = 308E35411E8C280700CAC09E /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTPushNotification.a;\n\t\t\tremoteRef = 308E359C1E8C2A3D00CAC09E /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t308E359F1E8C2A3D00CAC09E /* libRCTPushNotification-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTPushNotification-tvOS.a\";\n\t\t\tremoteRef = 308E359E1E8C2A3D00CAC09E /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t308E376F1E8D63B000CAC09E /* libBVLinearGradient.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libBVLinearGradient.a;\n\t\t\tremoteRef = 308E376E1E8D63B000CAC09E /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTImage-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTLinking-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTNetwork-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTSettings-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTText-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = \"libRCTWebSocket-tvOS.a\";\n\t\t\tremoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EA31DF850E9000B6D8A /* libReact.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libReact.a;\n\t\t\tremoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libyoga.a;\n\t\t\tremoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libyoga.a;\n\t\t\tremoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libcxxreact.a;\n\t\t\tremoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libcxxreact.a;\n\t\t\tremoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libjschelpers.a;\n\t\t\tremoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libjschelpers.a;\n\t\t\tremoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTAnimation.a;\n\t\t\tremoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTAnimation.a;\n\t\t\tremoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTLinking.a;\n\t\t\tremoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\t832341B51AAA6A8300B99B32 /* libRCTText.a */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = libRCTText.a;\n\t\t\tremoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t00E356EC1AD99517003FC87E /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t13B07F8E1A680F5B00A75B9A /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,\n\t\t\t\t309371D31E2DBEAE0019E3C2 /* 512@2x.png in Resources */,\n\t\t\t\t13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,\n\t\t\t\t3035251A1E4BFD4E0003A60D /* launchscreen.png in Resources */,\n\t\t\t\t3035251B1E4BFD4E0003A60D /* launchscreen@2x.png in Resources */,\n\t\t\t\t309371D11E2DBE160019E3C2 /* 512.png in Resources */,\n\t\t\t\t3035251C1E4BFD4E0003A60D /* launchscreen@3x.png in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E4791E0B4A5D006451C7 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E48E1E0B4A5D006451C7 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Bundle React Native code and images\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"export NODE_BINARY=node\\n../node_modules/react-native/packager/react-native-xcode.sh\";\n\t\t};\n\t\t2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Bundle React Native Code And Images\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"export NODE_BINARY=node\\n../node_modules/react-native/packager/react-native-xcode.sh\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t00E356EA1AD99517003FC87E /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t00E356F31AD99517003FC87E /* F82017Tests.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t13B07F871A680F5B00A75B9A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,\n\t\t\t\t13B07FC11A68108700A75B9A /* main.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E4771E0B4A5D006451C7 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,\n\t\t\t\t2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2D02E48C1E0B4A5D006451C7 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2DCD954D1E0B4F2C00145EB5 /* F82017Tests.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t00E356F51AD99517003FC87E /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 13B07F861A680F5B00A75B9A /* F82017 */;\n\t\t\ttargetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;\n\t\t};\n\t\t2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */;\n\t\t\ttargetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t13B07FB21A68108700A75B9A /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.xib;\n\t\t\tpath = F82017;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t00E356F61AD99517003FC87E /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = F82017Tests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 8.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tLIBRARY_SEARCH_PATHS = \"$(inherited)\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/F82017.app/F82017\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t00E356F71AD99517003FC87E /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = F82017Tests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 8.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tLIBRARY_SEARCH_PATHS = \"$(inherited)\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/F82017.app/F82017\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t13B07F941A680F5B00A75B9A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODEPUSH_KEY = \"o5k128-QbdToCO12IQl4zgJlxZQbVkEG2YjY-\";\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = F82017/F82017.entitlements;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEAD_CODE_STRIPPING = NO;\n\t\t\t\tDEVELOPMENT_TEAM = 9B8M3M76P9;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native/React/**\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = F82017/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.parse.f8;\n\t\t\t\tPRODUCT_NAME = F82017;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t13B07F951A680F5B00A75B9A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODEPUSH_KEY = \"qFZ_DANxpdRqMzw6anqVg9Z0D_RuVkEG2YjY-\";\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = F82017/F82017.entitlements;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 9B8M3M76P9;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native/React/**\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = F82017/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.parse.f8;\n\t\t\t\tPRODUCT_NAME = F82017;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2D02E4971E0B4A5E006451C7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = \"App Icon & Top Shelf Image\";\n\t\t\t\tASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = \"F82017-tvOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.facebook.REACT.F82017-tvOS\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = appletvos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 3;\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 9.2;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2D02E4981E0B4A5E006451C7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = \"App Icon & Top Shelf Image\";\n\t\t\t\tASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = \"F82017-tvOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-lc++\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.facebook.REACT.F82017-tvOS\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = appletvos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 3;\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 9.2;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2D02E4991E0B4A5E006451C7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tINFOPLIST_FILE = \"F82017-tvOSTests/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.facebook.REACT.F82017-tvOSTests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = appletvos;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/F82017-tvOS.app/F82017-tvOS\";\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 10.1;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2D02E49A1E0B4A5E006451C7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tINFOPLIST_FILE = \"F82017-tvOSTests/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.facebook.REACT.F82017-tvOSTests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSDKROOT = appletvos;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/F82017-tvOS.app/F82017-tvOS\";\n\t\t\t\tTVOS_DEPLOYMENT_TARGET = 10.1;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t83CBBA201A601CBA00E9B192 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"~/Documents/FacebookSDK\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_SYMBOLS_PRIVATE_EXTERN = NO;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = \"\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 8.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t83CBBA211A601CBA00E9B192 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = YES;\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = \"~/Documents/FacebookSDK\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = \"\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 8.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget \"F82017Tests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t00E356F61AD99517003FC87E /* Debug */,\n\t\t\t\t00E356F71AD99517003FC87E /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget \"F82017\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t13B07F941A680F5B00A75B9A /* Debug */,\n\t\t\t\t13B07F951A680F5B00A75B9A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget \"F82017-tvOS\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2D02E4971E0B4A5E006451C7 /* Debug */,\n\t\t\t\t2D02E4981E0B4A5E006451C7 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget \"F82017-tvOSTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2D02E4991E0B4A5E006451C7 /* Debug */,\n\t\t\t\t2D02E49A1E0B4A5E006451C7 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject \"F82017\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t83CBBA201A601CBA00E9B192 /* Debug */,\n\t\t\t\t83CBBA211A601CBA00E9B192 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;\n}\n"
  },
  {
    "path": "ios/F82017.xcodeproj/xcshareddata/xcschemes/F82017-tvOS.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0820\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"NO\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"2D2A28121D9B038B00D4039D\"\n               BuildableName = \"libReact.a\"\n               BlueprintName = \"React-tvOS\"\n               ReferencedContainer = \"container:../node_modules/react-native/React/React.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"2D02E47A1E0B4A5D006451C7\"\n               BuildableName = \"F82017-tvOS.app\"\n               BlueprintName = \"F82017-tvOS\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"2D02E48F1E0B4A5D006451C7\"\n               BuildableName = \"F82017-tvOSTests.xctest\"\n               BlueprintName = \"F82017-tvOSTests\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"2D02E48F1E0B4A5D006451C7\"\n               BuildableName = \"F82017-tvOSTests.xctest\"\n               BlueprintName = \"F82017-tvOSTests\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"2D02E47A1E0B4A5D006451C7\"\n            BuildableName = \"F82017-tvOS.app\"\n            BlueprintName = \"F82017-tvOS\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"2D02E47A1E0B4A5D006451C7\"\n            BuildableName = \"F82017-tvOS.app\"\n            BlueprintName = \"F82017-tvOS\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"2D02E47A1E0B4A5D006451C7\"\n            BuildableName = \"F82017-tvOS.app\"\n            BlueprintName = \"F82017-tvOS\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/F82017.xcodeproj/xcshareddata/xcschemes/F82017.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0620\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"NO\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"83CBBA2D1A601D0E00E9B192\"\n               BuildableName = \"libReact.a\"\n               BlueprintName = \"React\"\n               ReferencedContainer = \"container:../node_modules/react-native/React/React.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"13B07F861A680F5B00A75B9A\"\n               BuildableName = \"F82017.app\"\n               BlueprintName = \"F82017\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"00E356ED1AD99517003FC87E\"\n               BuildableName = \"F82017Tests.xctest\"\n               BlueprintName = \"F82017Tests\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"00E356ED1AD99517003FC87E\"\n               BuildableName = \"F82017Tests.xctest\"\n               BlueprintName = \"F82017Tests\"\n               ReferencedContainer = \"container:F82017.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"13B07F861A680F5B00A75B9A\"\n            BuildableName = \"F82017.app\"\n            BlueprintName = \"F82017\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"13B07F861A680F5B00A75B9A\"\n            BuildableName = \"F82017.app\"\n            BlueprintName = \"F82017\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"13B07F861A680F5B00A75B9A\"\n            BuildableName = \"F82017.app\"\n            BlueprintName = \"F82017\"\n            ReferencedContainer = \"container:F82017.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/F82017Tests/F82017Tests.m",
    "content": "/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#import <UIKit/UIKit.h>\n#import <XCTest/XCTest.h>\n\n#import <React/RCTLog.h>\n#import <React/RCTRootView.h>\n\n#define TIMEOUT_SECONDS 600\n#define TEXT_TO_LOOK_FOR @\"Welcome to React Native!\"\n\n@interface F82017Tests : XCTestCase\n\n@end\n\n@implementation F82017Tests\n\n- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test\n{\n  if (test(view)) {\n    return YES;\n  }\n  for (UIView *subview in [view subviews]) {\n    if ([self findSubviewInView:subview matching:test]) {\n      return YES;\n    }\n  }\n  return NO;\n}\n\n- (void)testRendersWelcomeScreen\n{\n  UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];\n  NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];\n  BOOL foundElement = NO;\n\n  __block NSString *redboxError = nil;\n  RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {\n    if (level >= RCTLogLevelError) {\n      redboxError = message;\n    }\n  });\n\n  while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {\n    [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];\n    [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];\n\n    foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {\n      if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {\n        return YES;\n      }\n      return NO;\n    }];\n  }\n\n  RCTSetLogFunction(RCTDefaultLogFunction);\n\n  XCTAssertNil(redboxError, @\"RedBox error: %@\", redboxError);\n  XCTAssertTrue(foundElement, @\"Couldn't find element with text '%@' in %d seconds\", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);\n}\n\n\n@end\n"
  },
  {
    "path": "ios/F82017Tests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "js/F8Analytics.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport { AppEventsLogger } from \"react-native-fbsdk\";\n\nexport default class F8Analytics {\n  static logEvent(name, value, opts) {\n    AppEventsLogger.logEvent(name, value, opts);\n  }\n}\n"
  },
  {
    "path": "js/F8App.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { AppState, StyleSheet, StatusBar, View } from \"react-native\";\nimport LoginScreen from \"./login/LoginScreen\";\nimport PushNotificationsController from \"./PushNotificationsController\";\nimport F8Navigator from \"./F8Navigator\";\nimport {\n  loadConfig,\n  loadMaps,\n  loadNotifications,\n  loadSessions,\n  loadFAQs,\n  loadPages,\n  loadFriendsSchedules,\n  loadSurveys,\n  loadVideos,\n  loadPolicies,\n  restoreSchedule\n} from \"./actions\";\nimport { updateInstallation } from \"./actions/installation\";\nimport { connect } from \"react-redux\";\nimport { version } from \"./env.js\";\n\nclass F8App extends React.Component {\n  componentDidMount() {\n    AppState.addEventListener(\"change\", this.handleAppStateChange);\n\n    // TODO: Make this list smaller, we basically download the whole internet\n    this.props.dispatch(loadSessions());\n    this.props.dispatch(loadConfig());\n    this.props.dispatch(loadNotifications());\n    this.props.dispatch(loadVideos());\n    this.props.dispatch(loadMaps());\n    this.props.dispatch(loadFAQs());\n    this.props.dispatch(loadPages());\n    this.props.dispatch(loadPolicies());\n\n    if (this.props.isLoggedIn) {\n      this.props.dispatch(restoreSchedule());\n      this.props.dispatch(loadSurveys());\n      this.props.dispatch(loadFriendsSchedules());\n    }\n\n    updateInstallation({ version });\n  }\n\n  componentWillUnmount() {\n    AppState.removeEventListener(\"change\", this.handleAppStateChange);\n  }\n\n  handleAppStateChange = appState => {\n    if (appState === \"active\") {\n      this.props.dispatch(loadSessions());\n      this.props.dispatch(loadVideos());\n      this.props.dispatch(loadNotifications());\n\n      if (this.props.isLoggedIn) {\n        this.props.dispatch(restoreSchedule());\n        this.props.dispatch(loadSurveys());\n      }\n    }\n  };\n\n  render() {\n    if (!this.props.skipWelcomeScreen) {\n      return <LoginScreen />;\n    }\n    return (\n      <View style={styles.container}>\n        <StatusBar\n          hidden={false}\n          translucent={true}\n          backgroundColor=\"rgba(0, 0, 0, 0)\"\n          barStyle=\"light-content\"\n        />\n        <F8Navigator />\n        <PushNotificationsController />\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1\n  }\n});\n\nfunction select(store) {\n  return {\n    isLoggedIn: store.user.isLoggedIn,\n    skipWelcomeScreen: store.user.isLoggedIn || store.user.hasSkippedLogin\n  };\n}\n\nmodule.exports = connect(select)(F8App);\n"
  },
  {
    "path": "js/F8Navigator.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport Platform from \"Platform\";\nimport BackAndroid from \"BackAndroid\";\nimport F8TabsView from \"./tabs/F8TabsView\";\nimport FriendsScheduleView from \"./tabs/schedule/FriendsScheduleView\";\nimport FilterScreen from \"./filter/FilterScreen\";\nimport LoginModal from \"./login/LoginModal\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport SessionsCarousel from \"./tabs/schedule/SessionsCarousel\";\n\nimport SharingSettingsScreen from \"./tabs/schedule/SharingSettingsScreen\";\n\nimport F8WebView from \"./common/F8WebView\";\nimport RatingScreen from \"./rating/RatingScreen\";\nimport { StyleSheet } from \"react-native\";\nimport { connect } from \"react-redux\";\n\nimport F8Colors from \"./common/F8Colors\";\nimport F8VideoView from \"./tabs/videos/F8VideoView\";\nimport { switchTab } from \"./actions\";\nimport F8MapView from \"./tabs/maps/F8MapView\";\nimport DemosCarousel from \"./tabs/demos/DemosCarousel\";\n\nconst F8Navigator = React.createClass({\n  _handlers: ([]: Array<() => boolean>),\n\n  componentDidMount: function() {\n    BackAndroid.addEventListener(\"hardwareBackPress\", this.handleBackButton);\n  },\n\n  componentWillUnmount: function() {\n    BackAndroid.removeEventListener(\"hardwareBackPress\", this.handleBackButton);\n  },\n\n  getChildContext() {\n    return {\n      addBackButtonListener: this.addBackButtonListener,\n      removeBackButtonListener: this.removeBackButtonListener\n    };\n  },\n\n  addBackButtonListener: function(listener) {\n    this._handlers.push(listener);\n  },\n\n  removeBackButtonListener: function(listener) {\n    this._handlers = this._handlers.filter(handler => handler !== listener);\n  },\n\n  handleBackButton: function() {\n    for (let i = this._handlers.length - 1; i >= 0; i--) {\n      if (this._handlers[i]()) {\n        return true;\n      }\n    }\n\n    const navigator = this._navigator;\n    if (navigator && navigator.getCurrentRoutes().length > 1) {\n      navigator.pop();\n      return true;\n    }\n\n    if (this.props.tab !== \"schedule\") {\n      this.props.dispatch(switchTab(\"schedule\"));\n      return true;\n    }\n    return false;\n  },\n\n  render: function() {\n    return (\n      <Navigator\n        ref={c => (this._navigator = c)}\n        style={styles.container}\n        configureScene={route => {\n          if (Platform.OS === \"android\") {\n            return Navigator.SceneConfigs.FloatFromBottomAndroid;\n          }\n          // TODO: Proper scene support\n          if (\n            route.shareSettings ||\n            route.friend ||\n            route.webview ||\n            route.video ||\n            route.session ||\n            route.allSession ||\n            route.allDemos\n          ) {\n            return Navigator.SceneConfigs.PushFromRight;\n          } else {\n            return Navigator.SceneConfigs.FloatFromBottom;\n          }\n        }}\n        initialRoute={{}}\n        renderScene={this.renderScene}\n      />\n    );\n  },\n\n  renderScene: function(route, navigator) {\n    if (route.allSessions) {\n      return <SessionsCarousel {...route} navigator={navigator} />;\n    } else if (route.session) {\n      return <SessionsCarousel session={route.session} navigator={navigator} />;\n    } else if (route.filter) {\n      return <FilterScreen navigator={navigator} {...route} />;\n    } else if (route.friend) {\n      return (\n        <FriendsScheduleView friend={route.friend} navigator={navigator} />\n      );\n    } else if (route.login) {\n      return <LoginModal navigator={navigator} onLogin={route.callback} />;\n    } else if (route.shareSettings) {\n      // else if (route.share){ return <SharingSettingsModal navigator={navigator} />; }\n      return <SharingSettingsScreen navigator={navigator} />;\n    } else if (route.rate) {\n      return <RatingScreen navigator={navigator} survey={route.survey} />;\n    } else if (route.webview) {\n      return <F8WebView {...route} url={route.webview} navigator={navigator} />;\n    } else if (route.video) {\n      return <F8VideoView video={route.video} navigator={navigator} />;\n    } else if (route.maps) {\n      return <F8MapView directions={false} navigator={navigator} />;\n    } else if (route.allDemos) {\n      return <DemosCarousel {...route} navigator={navigator} />;\n    } else {\n      return <F8TabsView navigator={navigator} />;\n    }\n  }\n});\n\nF8Navigator.childContextTypes = {\n  addBackButtonListener: React.PropTypes.func,\n  removeBackButtonListener: React.PropTypes.func\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.bianca\n  }\n});\n\nfunction select(store) {\n  return {\n    tab: store.navigation.tab,\n    isLoggedIn: store.user.isLoggedIn || store.user.hasSkippedLogin\n  };\n}\n\nmodule.exports = connect(select)(F8Navigator);\n"
  },
  {
    "path": "js/FacebookSDK.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * This module implements a part of Facebook JavaScript SDK\n * https://developers.facebook.com/docs/javascript/reference/v2.2\n *\n * @flow\n */\n\"use strict\";\n\nimport {\n  LoginManager,\n  AccessToken,\n  GraphRequest,\n  GraphRequestManager\n} from \"react-native-fbsdk\";\n\nconst emptyFunction = () => {};\nimport mapObject from \"fbjs/lib/mapObject\";\n\ntype AuthResponse = {\n  userID: string,\n  accessToken: string,\n  expiresIn: number\n};\ntype LoginOptions = { scope: string };\ntype LoginCallback = (result: {\n  authResponse?: AuthResponse,\n  error?: Error\n}) => void;\n\nlet _authResponse: ?AuthResponse = null;\n\nasync function loginWithFacebookSDK(\n  options: LoginOptions\n): Promise<AuthResponse> {\n  const scope = options.scope || \"public_profile\";\n  const permissions = scope.split(\",\");\n\n  const loginResult = await LoginManager.logInWithReadPermissions(permissions);\n  if (loginResult.isCancelled) {\n    throw new Error(\"Canceled by user\");\n  }\n\n  const accessToken = await AccessToken.getCurrentAccessToken();\n  if (!accessToken) {\n    throw new Error(\"No access token\");\n  }\n\n  _authResponse = {\n    userID: accessToken.userID, // FIXME: RNFBSDK bug: userId -> userID\n    accessToken: accessToken.accessToken,\n    expiresIn: Math.round((accessToken.expirationTime - Date.now()) / 1000)\n  };\n  return _authResponse;\n}\n\nconst FacebookSDK = {\n  init() {\n    // This is needed by Parse\n    window.FB = FacebookSDK;\n  },\n\n  login(callback: LoginCallback, options: LoginOptions) {\n    loginWithFacebookSDK(options).then(\n      authResponse => callback({ authResponse }),\n      error => callback({ error })\n    );\n  },\n\n  getAuthResponse(): ?AuthResponse {\n    return _authResponse;\n  },\n\n  logout() {\n    LoginManager.logOut();\n  },\n\n  /**\n   * Make a API call to Graph server. This is the **real** RESTful API.\n   *\n   * Except the path, all arguments to this function are optional. So any of\n   * these are valid:\n   *\n   *   FB.api('/me') // throw away the response\n   *   FB.api('/me', function(r) { console.log(r) })\n   *   FB.api('/me', { fields: 'email' }); // throw away response\n   *   FB.api('/me', { fields: 'email' }, function(r) { console.log(r) });\n   *   FB.api('/12345678', 'delete', function(r) { console.log(r) });\n   *   FB.api(\n   *     '/me/feed',\n   *     'post',\n   *     { body: 'hi there' },\n   *     function(r) { console.log(r) }\n   *   );\n   *\n   * param path   {String}   the url path\n   * param method {String}   the http method\n   * param params {Object}   the parameters for the query\n   * param cb     {Function} the callback function to handle the response\n   */\n  api: function(path: string, ...args: Array<mixed>) {\n    const argByType = {};\n    args.forEach(arg => {\n      argByType[typeof arg] = arg;\n    });\n\n    const httpMethod = (argByType.string || \"get\").toUpperCase();\n    const params = argByType.object || {};\n    const callback = argByType.function || emptyFunction;\n\n    // FIXME: Move this into RNFBSDK\n    // GraphRequest requires all parameters to be in {string: 'abc'}\n    // or {uri: 'xyz'} format\n    const parameters = mapObject(params, value => ({ string: value }));\n\n    function processResponse(error, result) {\n      // FIXME: RNFBSDK bug: result is Object on iOS and string on Android\n      if (!error && typeof result === \"string\") {\n        try {\n          result = JSON.parse(result);\n        } catch (e) {\n          error = e;\n        }\n      }\n\n      const data = error ? { error } : result;\n      callback(data);\n    }\n\n    const request = new GraphRequest(\n      path,\n      { parameters, httpMethod },\n      processResponse\n    );\n    new GraphRequestManager().addRequest(request).start();\n  }\n};\n\nmodule.exports = FacebookSDK;\n"
  },
  {
    "path": "js/Playground.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport View from \"View\";\n\nclass Playground extends React.Component {\n  state: {\n    content: Array<ReactElement>\n  };\n\n  constructor() {\n    super();\n    const content = [];\n    const define = (name: string, render: Function) => {\n      content.push(<Example key={name} render={render} />);\n    };\n    // const Module = require('F8PageControl');\n    const Module = require(\"F8Header\");\n    // const Module = require('./tabs/schedule/AddToScheduleButton');\n    // const Module = require('./rating/Header');\n    // $FlowFixMe: doesn't understand static\n    Module.__cards__(define);\n    this.state = { content };\n  }\n\n  render() {\n    return (\n      <View style={{ backgroundColor: \"#336699\", flex: 1 }}>\n        {this.state.content}\n      </View>\n    );\n  }\n}\n\nclass Example extends React.Component {\n  state = {\n    inner: null\n  };\n\n  render() {\n    const content = this.props.render(this.state.inner, inner =>\n      this.setState({ inner })\n    );\n    return <View>{content}</View>;\n  }\n}\n\nmodule.exports = Playground;\n"
  },
  {
    "path": "js/PushNotificationsController.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport unseenNotificationsCount from \"./tabs/notifications/unseenNotificationsCount\";\nimport { AppState, Platform, PushNotificationIOS } from \"react-native\";\n\n// $FlowIssue\nimport PushNotification from \"react-native-push-notification\";\n\nimport { connect } from \"react-redux\";\nimport {\n  storeDeviceToken,\n  receivePushNotification,\n  updateInstallation,\n  markAllNotificationsAsSeen\n} from \"./actions\";\n\nimport type { Dispatch } from \"./actions/types\";\n\nimport { gcmSenderId } from \"./env\";\n\nclass AppBadgeController extends React.Component {\n  props: {\n    tab: string,\n    enabled: boolean,\n    badge: number,\n    dispatch: Dispatch\n  };\n\n  handleAppStateChange = appState => {\n    if (appState === \"active\") {\n      this.updateAppBadge();\n      if (this.props.tab === \"info\") {\n        this.eventuallyMarkNotificationsAsSeen();\n      }\n    }\n  };\n\n  componentDidMount() {\n    AppState.addEventListener(\"change\", this.handleAppStateChange);\n\n    const { dispatch } = this.props;\n    PushNotification.configure({\n      onRegister: ({ token }) => dispatch(storeDeviceToken(token)),\n      onNotification: notif => dispatch(receivePushNotification(notif)),\n      senderID: gcmSenderId,\n      requestPermissions: this.props.enabled\n    });\n\n    this.updateAppBadge();\n  }\n\n  componentWillUnmount() {\n    AppState.removeEventListener(\"change\", this.handleAppStateChange);\n  }\n\n  componentDidUpdate(prevProps) {\n    if (!prevProps.enabled && this.props.enabled) {\n      PushNotification.requestPermissions();\n    }\n    if (this.props.badge !== prevProps.badge) {\n      this.updateAppBadge();\n    }\n    if (this.props.tab === \"info\" && prevProps.tab !== \"info\") {\n      this.eventuallyMarkNotificationsAsSeen();\n    }\n  }\n\n  updateAppBadge() {\n    if (this.props.enabled && Platform.OS === \"ios\") {\n      PushNotificationIOS.setApplicationIconBadgeNumber(this.props.badge);\n      updateInstallation({ badge: this.props.badge });\n    }\n  }\n\n  eventuallyMarkNotificationsAsSeen() {\n    const { dispatch } = this.props;\n    setTimeout(() => dispatch(markAllNotificationsAsSeen()), 1000);\n  }\n\n  render() {\n    return null;\n  }\n}\n\nfunction select(store) {\n  return {\n    enabled: store.notifications.enabled === true,\n    badge: unseenNotificationsCount(store),\n    tab: store.navigation.tab\n  };\n}\n\nmodule.exports = connect(select)(AppBadgeController);\n"
  },
  {
    "path": "js/actions/config.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\nimport { InteractionManager } from \"react-native\";\n\nimport type { Action } from \"./types\";\n\nasync function loadConfig(): Promise<Action> {\n  const config = await Parse.Config.get();\n  await InteractionManager.runAfterInteractions();\n  return {\n    type: \"LOADED_CONFIG\",\n    config\n  };\n}\n\nmodule.exports = { loadConfig };\n"
  },
  {
    "path": "js/actions/filter.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport type { Action } from \"./types\";\n\ntype Schedule = { [key: string]: boolean };\ntype Video = { [key: string]: boolean };\n\nfunction applyScheduleFilter(scheduleTopics: Schedule): Action {\n  return {\n    type: \"APPLY_SCHEDULE_TOPICS_FILTER\",\n    scheduleTopics\n  };\n}\n\nfunction clearScheduleFilter(): Action {\n  return {\n    type: \"CLEAR_SCHEDULE_FILTER\"\n  };\n}\n\nfunction applyVideoFilter(videoTopics: Video): Action {\n  return {\n    type: \"APPLY_VIDEO_TOPICS_FILTER\",\n    videoTopics\n  };\n}\n\nfunction clearVideoFilter(): Action {\n  return {\n    type: \"CLEAR_VIDEO_FILTER\"\n  };\n}\n\nmodule.exports = {\n  applyScheduleFilter,\n  clearScheduleFilter,\n  applyVideoFilter,\n  clearVideoFilter\n};\n"
  },
  {
    "path": "js/actions/index.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport * as parseActions from \"./parse\";\nimport * as navigationActions from \"./navigation\";\nimport * as loginActions from \"./login\";\nimport * as scheduleActions from \"./schedule\";\nimport * as filterActions from \"./filter\";\nimport * as notificationActions from \"./notifications\";\nimport * as configActions from \"./config\";\nimport * as surveyActions from \"./surveys\";\nimport * as testActions from \"./test\";\nimport * as installationActions from \"./installation\";\nimport * as videoActions from \"./video\";\n\nmodule.exports = {\n  ...loginActions,\n  ...scheduleActions,\n  ...filterActions,\n  ...notificationActions,\n  ...configActions,\n  ...surveyActions,\n  ...testActions,\n  ...parseActions,\n  ...navigationActions,\n  ...installationActions,\n  ...videoActions\n};\n"
  },
  {
    "path": "js/actions/installation.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { Platform } from \"react-native\";\nimport Parse from \"parse/react-native\";\n\nasync function currentInstallation(): Promise<Parse.Installation> {\n  const installationId = await Parse._getInstallationId();\n  return new Parse.Installation({\n    installationId,\n    appName: \"F8\",\n    deviceType: Platform.OS,\n    // TODO: Get this information from the app itself\n    appIdentifier: Platform.OS === \"ios\" ? \"com.parse.f8\" : \"com.facebook.f8\"\n  });\n}\n\nasync function updateInstallation(updates: Object = {}): Promise<void> {\n  const installation = await currentInstallation();\n  await installation.save(updates);\n}\n\nmodule.exports = { currentInstallation, updateInstallation };\n"
  },
  {
    "path": "js/actions/login.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\nimport FacebookSDK from \"../FacebookSDK\";\nimport ActionSheetIOS from \"ActionSheetIOS\";\nimport { Platform } from \"react-native\";\nimport Alert from \"Alert\";\nimport { restoreSchedule, loadFriendsSchedules } from \"./schedule\";\nimport { updateInstallation } from \"./installation\";\nimport { loadSurveys } from \"./surveys\";\n\nimport type { Action, ThunkAction } from \"./types\";\n\nasync function ParseFacebookLogin(scope): Promise {\n  return new Promise((resolve, reject) => {\n    Parse.FacebookUtils.logIn(scope, {\n      success: resolve,\n      error: (user, error) => reject((error && error.error) || error)\n    });\n  });\n}\n\nasync function queryFacebookAPI(path, ...args): Promise {\n  return new Promise((resolve, reject) => {\n    FacebookSDK.api(path, ...args, response => {\n      if (response && !response.error) {\n        resolve(response);\n      } else {\n        reject(response && response.error);\n      }\n    });\n  });\n}\n\nasync function _logInWithFacebook(source: ?string): Promise<Array<Action>> {\n  await ParseFacebookLogin(\"public_profile,email,user_friends\");\n  const profile = await queryFacebookAPI(\"/me\", { fields: \"name,email,link\" });\n\n  const user = await Parse.User.currentAsync();\n  user.set(\"facebook_id\", profile.id);\n  user.set(\"name\", profile.name);\n  user.set(\"email\", profile.email);\n  user.set(\"link\", profile.link);\n  await user.save();\n  await updateInstallation({ user });\n\n  const action = {\n    type: \"LOGGED_IN\",\n    source,\n    data: {\n      id: profile.id,\n      name: profile.name,\n      sharedSchedule: user.get(\"sharedSchedule\")\n    }\n  };\n\n  return Promise.all([Promise.resolve(action), restoreSchedule()]);\n}\n\nfunction logInWithFacebook(source: ?string): ThunkAction {\n  return dispatch => {\n    const login = _logInWithFacebook(source);\n\n    // Loading friends schedules shouldn't block the login process\n    login.then(result => {\n      dispatch(result);\n      dispatch(loadFriendsSchedules());\n      dispatch(loadSurveys());\n    });\n    return login;\n  };\n}\n\nfunction skipLogin(): Action {\n  return {\n    type: \"SKIPPED_LOGIN\"\n  };\n}\n\nfunction logOut(): ThunkAction {\n  return dispatch => {\n    Parse.User.logOut();\n    FacebookSDK.logout();\n    updateInstallation({ user: null, channels: [] });\n\n    // TODO: Make sure reducers clear their state\n    return dispatch({\n      type: \"LOGGED_OUT\"\n    });\n  };\n}\n\nfunction logOutWithPrompt(): ThunkAction {\n  return (dispatch, getState) => {\n    let name = getState().user.name || \"there\";\n\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showActionSheetWithOptions(\n        {\n          title: `Hi, ${name}`,\n          options: [\"Log out\", \"Cancel\"],\n          destructiveButtonIndex: 0,\n          cancelButtonIndex: 1\n        },\n        buttonIndex => {\n          if (buttonIndex === 0) {\n            dispatch(logOut());\n          }\n        }\n      );\n    } else {\n      Alert.alert(`Hi, ${name}`, \"Log out from F8?\", [\n        { text: \"Cancel\" },\n        { text: \"Log out\", onPress: () => dispatch(logOut()) }\n      ]);\n    }\n  };\n}\n\nmodule.exports = { logInWithFacebook, skipLogin, logOut, logOutWithPrompt };\n"
  },
  {
    "path": "js/actions/navigation.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport type { Action } from \"./types\";\n\ntype Tab = \"schedule\" | \"myF8\" | \"demos\" | \"videos\" | \"info\";\n\nmodule.exports = {\n  switchTab: (tab: Tab): Action => ({\n    type: \"SWITCH_TAB\",\n    tab\n  }),\n\n  switchDay: (day: 1 | 2): Action => ({\n    type: \"SWITCH_DAY\",\n    day\n  })\n};\n"
  },
  {
    "path": "js/actions/notifications.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { Platform, VibrationIOS } from \"react-native\";\nimport { updateInstallation } from \"./installation\";\nimport { loadNotifications } from \"./parse\";\nimport { loadSurveys } from \"./surveys\";\nimport { switchTab } from \"./navigation\";\n\nimport type { Action, ThunkAction } from \"./types\";\n\ntype PushNotification = {\n  foreground: boolean,\n  message: string,\n  // react-native-push-notification library sends Object as data\n  // on iOS and JSON string on android\n  // TODO: Send PR to remove this inconsistency\n  data: string | Object\n};\n\nfunction normalizeData(s: string | Object): Object {\n  if (s && typeof s === \"object\") {\n    return s;\n  }\n  try {\n    return JSON.parse(s);\n  } catch (e) {\n    return {};\n  }\n}\n\nasync function storeDeviceToken(deviceToken: string): Promise<Action> {\n  const pushType = Platform.OS === \"android\" ? \"gcm\" : undefined;\n  await updateInstallation({\n    pushType,\n    deviceToken,\n    deviceTokenLastModified: Date.now()\n  });\n  return {\n    type: \"REGISTERED_PUSH_NOTIFICATIONS\"\n  };\n}\n\nfunction turnOnPushNotifications(): Action {\n  return {\n    type: \"TURNED_ON_PUSH_NOTIFICATIONS\"\n  };\n}\n\nfunction skipPushNotifications(): Action {\n  return {\n    type: \"SKIPPED_PUSH_NOTIFICATIONS\"\n  };\n}\n\nfunction receivePushNotification(notification: PushNotification): ThunkAction {\n  return dispatch => {\n    const { foreground, message } = notification;\n    const data = normalizeData(notification.data);\n\n    if (!foreground) {\n      dispatch(switchTab(\"info\"));\n    }\n\n    if (foreground) {\n      dispatch(loadNotifications());\n      dispatch(loadSurveys());\n\n      if (Platform.OS === \"ios\") {\n        VibrationIOS.vibrate();\n      }\n    }\n\n    if (data.e /* ephemeral */) {\n      return;\n    }\n\n    const timestamp = new Date().getTime();\n    dispatch({\n      type: \"RECEIVED_PUSH_NOTIFICATION\",\n      notification: {\n        text: message,\n        url: data.url,\n        urlTitle: data.urlTitle,\n        image: data.image,\n        video: data.video,\n        time: timestamp\n      }\n    });\n  };\n}\n\nfunction markAllNotificationsAsSeen(): Action {\n  return {\n    type: \"SEEN_ALL_NOTIFICATIONS\"\n  };\n}\n\nmodule.exports = {\n  turnOnPushNotifications,\n  storeDeviceToken,\n  skipPushNotifications,\n  receivePushNotification,\n  markAllNotificationsAsSeen\n};\n"
  },
  {
    "path": "js/actions/parse.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\nimport { logError, InteractionManager } from \"react-native\";\n\nimport type { ThunkAction } from \"./types\";\n\nconst Maps = Parse.Object.extend(\"Maps\");\nconst Notification = Parse.Object.extend(\"Notification\");\nconst FAQ = Parse.Object.extend(\"FAQ\");\nconst Page = Parse.Object.extend(\"Page\");\nconst Video = Parse.Object.extend(\"Video\");\nconst Policy = Parse.Object.extend(\"Policy\");\n\nfunction loadParseQuery(type: string, query: Parse.Query): ThunkAction {\n  return dispatch => {\n    return query.find({\n      success: list => {\n        // We don't want data loading to interfere with smooth animations\n        InteractionManager.runAfterInteractions(() => {\n          // Flow can't guarantee {type, list} is a valid action\n          dispatch(({ type, list }: any));\n        });\n      },\n      error: logError\n    });\n  };\n}\n\nfunction loadSessions(): ThunkAction {\n  return loadParseQuery(\n    \"LOADED_SESSIONS\",\n    new Parse.Query(\"Agenda\").include(\"speakers\").ascending(\"startTime\")\n  );\n}\n\nfunction loadMaps(): ThunkAction {\n  return loadParseQuery(\"LOADED_MAPS\", new Parse.Query(Maps));\n}\n\nfunction loadNotifications(): ThunkAction {\n  return loadParseQuery(\"LOADED_NOTIFICATIONS\", new Parse.Query(Notification));\n}\n\nfunction loadFAQs(): ThunkAction {\n  return loadParseQuery(\n    \"LOADED_FAQS\",\n    new Parse.Query(FAQ).ascending(\"updatedAt\")\n  );\n}\n\nfunction loadPages(): ThunkAction {\n  return loadParseQuery(\n    \"LOADED_PAGES\",\n    new Parse.Query(Page).ascending(\"title\")\n  );\n}\n\nfunction loadVideos(): ThunkAction {\n  return loadParseQuery(\n    \"LOADED_VIDEOS\",\n    new Parse.Query(Video).descending(\"updatedAt\")\n  );\n}\n\nfunction loadPolicies(): ThunkAction {\n  return loadParseQuery(\n    \"LOADED_POLICIES\",\n    new Parse.Query(Policy).ascending(\"title\")\n  );\n}\n\nexport {\n  loadSessions,\n  loadMaps,\n  loadNotifications,\n  loadFAQs,\n  loadPages,\n  loadVideos,\n  loadPolicies\n};\n"
  },
  {
    "path": "js/actions/schedule.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\nimport F8Analytics from \"../F8Analytics\";\nimport {\n  Platform,\n  InteractionManager,\n  ActionSheetIOS,\n  Alert,\n  Share\n} from \"react-native\";\nconst Agenda = Parse.Object.extend(\"Agenda\");\nimport { currentInstallation, updateInstallation } from \"./installation\";\n\nimport type { ThunkAction, PromiseAction, Dispatch } from \"./types\";\nimport type { Session } from \"../reducers/sessions\";\n\nfunction addToSchedule(id: string): ThunkAction {\n  return (dispatch: Dispatch) => {\n    if (Parse.User.current()) {\n      Parse.User\n        .current()\n        .relation(\"mySchedule\")\n        .add(new Agenda({ id }));\n      Parse.User.current().save();\n      currentInstallation().then(installation => {\n        installation.addUnique(\"channels\", `session_${id}`);\n        return installation.save();\n      });\n    }\n    dispatch({\n      type: \"SESSION_ADDED\",\n      id\n    });\n  };\n}\n\nfunction removeFromSchedule(id: string): ThunkAction {\n  return (dispatch: Dispatch) => {\n    if (Parse.User.current()) {\n      Parse.User\n        .current()\n        .relation(\"mySchedule\")\n        .remove(new Agenda({ id }));\n      Parse.User.current().save();\n      currentInstallation().then(installation => {\n        installation.remove(\"channels\", `session_${id}`);\n        return installation.save();\n      });\n    }\n    dispatch({\n      type: \"SESSION_REMOVED\",\n      id\n    });\n  };\n}\n\nfunction removeFromScheduleWithPrompt(session: Session): ThunkAction {\n  return dispatch => {\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showActionSheetWithOptions(\n        {\n          options: [\"Remove From Schedule\", \"Cancel\"],\n          destructiveButtonIndex: 0,\n          cancelButtonIndex: 1\n        },\n        buttonIndex => {\n          if (buttonIndex === 0) {\n            dispatch(removeFromSchedule(session.id));\n          }\n        }\n      );\n    } else {\n      Alert.alert(\n        \"Remove From Your Schedule\",\n        `Would you like to remove \"${session.title}\" from your schedule?`,\n        [\n          { text: \"Cancel\" },\n          {\n            text: \"Remove\",\n            onPress: () => dispatch(removeFromSchedule(session.id))\n          }\n        ]\n      );\n    }\n  };\n}\n\nasync function restoreSchedule(): PromiseAction {\n  const list = await Parse.User\n    .current()\n    .relation(\"mySchedule\")\n    .query()\n    .find();\n  const channels = list.map(({ id }) => `session_${id}`);\n  updateInstallation({ channels });\n\n  return {\n    type: \"RESTORED_SCHEDULE\",\n    list\n  };\n}\n\nasync function loadFriendsSchedules(): PromiseAction {\n  const list = await Parse.Cloud.run(\"friends\");\n  await InteractionManager.runAfterInteractions();\n  return {\n    type: \"LOADED_FRIENDS_SCHEDULES\",\n    list\n  };\n}\n\nfunction setSharingEnabled(enabled: boolean): ThunkAction {\n  return dispatch => {\n    dispatch({\n      type: \"SET_SHARING\",\n      enabled\n    });\n    Parse.User.current().set(\"sharedSchedule\", enabled);\n    Parse.User.current().save();\n  };\n}\n\nfunction shareSession(session: Session): ThunkAction {\n  return (dispatch, getState) => {\n    const { sessionURLTemplate } = getState().config;\n    const url = sessionURLTemplate\n      .replace(\"{slug}\", session.slug)\n      .replace(\"{id}\", session.id);\n\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showShareActionSheetWithOptions(\n        {\n          message: session.title,\n          url\n        },\n        e => {},\n        logShare.bind(null, session.id)\n      );\n    } else {\n      Share.share(\n        {\n          // content\n          title: session.title,\n          message: url\n        },\n        {\n          // options\n          dialogTitle: \"Share Link to \" + session.title // droid-only share option\n        }\n      ).then(\n        // callback\n        _ => logShare(session.id, true, null)\n      );\n    }\n  };\n}\n\nfunction logShare(id, completed, activity) {\n  F8Analytics.logEvent(\"Share Session\", 1, { id });\n  // Parse.Analytics.track('share', {\n  //   id,\n  //   completed: completed ? 'yes' : 'no',\n  //   activity: activity || '?'\n  // });\n}\n\nmodule.exports = {\n  shareSession,\n  addToSchedule,\n  removeFromSchedule,\n  restoreSchedule,\n  loadFriendsSchedules,\n  setSharingEnabled,\n  removeFromScheduleWithPrompt\n};\n"
  },
  {
    "path": "js/actions/surveys.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\n\nimport type { Action } from \"./types\";\n\nasync function loadSurveys(): Promise<Action> {\n  const list = await Parse.Cloud.run(\"surveys\");\n  return {\n    type: \"LOADED_SURVEYS\",\n    list\n  };\n}\n\nasync function submitSurveyAnswers(\n  id: string,\n  answers: Array<any>\n): Promise<Action> {\n  await Parse.Cloud.run(\"submit_survey\", { id, answers });\n  return {\n    type: \"SUBMITTED_SURVEY_ANSWERS\",\n    id\n  };\n}\n\nmodule.exports = {\n  loadSurveys,\n  submitSurveyAnswers\n};\n"
  },
  {
    "path": "js/actions/test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport Parse from \"parse/react-native\";\nimport { Alert, ActionSheetIOS, Platform } from \"react-native\";\nimport { version } from \"../env\";\n\nimport type { Action, ThunkAction } from \"./types\";\n\nfunction testPlainPush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_push\");\n}\n\nfunction testLinkPush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_push\", { url: \"link\" });\n}\n\nfunction testSessionPush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_push\", { url: \"session\" });\n}\n\nfunction testImagePush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_push\", { url: \"image\" });\n}\nfunction testVideoPush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_push\", { url: \"video\" });\n}\n\nfunction testSurveyPush(): ThunkAction {\n  return () => Parse.Cloud.run(\"test_survey\");\n}\n\nfunction testResetNuxes(): Action {\n  return {\n    type: \"RESET_NUXES\"\n  };\n}\n\nfunction getAppInfo(): ThunkAction {\n  return (dispatch, getState) => {\n    const subject = `App v${version} state`;\n    const message = JSON.stringify(getState(), undefined, 2);\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showShareActionSheetWithOptions(\n        {\n          subject: subject,\n          message: message\n        },\n        () => {},\n        () => {}\n      );\n    } else {\n      Alert.alert(subject);\n    }\n  };\n}\n\nfunction testSetCurrentDate(value: ?number): Action {\n  return {\n    type: \"SET_TIMED_TESTING\",\n    value\n  };\n}\n\nconst TEST_MENU = {\n  \"Request a push notification\": testPlainPush,\n  \"Push with link\": testLinkPush,\n  \"Push with session\": testSessionPush,\n  \"Push with image\": testImagePush,\n  \"Push with video\": testVideoPush,\n  \"Request a survey\": testSurveyPush,\n  \"Reset NUXes\": testResetNuxes,\n  \"Get app info\": getAppInfo,\n  \"Set current date: Day 1\": _ => testSetCurrentDate(1),\n  \"Set current date: Day 2\": _ => testSetCurrentDate(2),\n  \"Reset current date\": _ => testSetCurrentDate(null)\n};\n\nmodule.exports = { TEST_MENU };\n"
  },
  {
    "path": "js/actions/types.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\ntype ParseObject = Object;\n\nexport type Action =\n  | { type: \"LOADED_ABOUT\", list: Array<ParseObject> }\n  | { type: \"LOADED_NOTIFICATIONS\", list: Array<ParseObject> }\n  | { type: \"LOADED_MAPS\", list: Array<ParseObject> }\n  | {\n      type: \"LOADED_FRIENDS_SCHEDULES\",\n      list: Array<{\n        id: string,\n        name: string,\n        schedule: { [key: string]: boolean }\n      }>\n    }\n  | { type: \"LOADED_CONFIG\", config: ParseObject }\n  | { type: \"LOADED_SESSIONS\", list: Array<ParseObject> }\n  | { type: \"LOADED_SURVEYS\", list: Array<Object> }\n  | { type: \"SUBMITTED_SURVEY_ANSWERS\", id: string }\n  | {\n      type: \"LOGGED_IN\",\n      source: ?string,\n      data: { id: string, name: string, sharedSchedule: ?boolean }\n    }\n  | { type: \"RESTORED_SCHEDULE\", list: Array<ParseObject> }\n  | { type: \"SKIPPED_LOGIN\" }\n  | { type: \"LOGGED_OUT\" }\n  | { type: \"SESSION_ADDED\", id: string }\n  | { type: \"SESSION_REMOVED\", id: string }\n  | { type: \"SET_SHARING\", enabled: boolean }\n  | { type: \"APPLY_TOPICS_FILTER\", topics: { [key: string]: boolean } }\n  | { type: \"CLEAR_FILTER\" }\n  | { type: \"SWITCH_DAY\", day: 1 | 2 }\n  | {\n      type: \"SWITCH_TAB\",\n      tab: \"schedule\" | \"my-schedule\" | \"map\" | \"notifications\" | \"info\"\n    }\n  | { type: \"TURNED_ON_PUSH_NOTIFICATIONS\" }\n  | { type: \"REGISTERED_PUSH_NOTIFICATIONS\" }\n  | { type: \"SKIPPED_PUSH_NOTIFICATIONS\" }\n  | { type: \"RECEIVED_PUSH_NOTIFICATION\", notification: Object }\n  | { type: \"SEEN_ALL_NOTIFICATIONS\" }\n  | { type: \"RESET_NUXES\" };\n\nexport type Dispatch = (\n  action: Action | ThunkAction | PromiseAction | Array<Action>\n) => any;\nexport type GetState = () => Object;\nexport type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;\nexport type PromiseAction = Promise<Action>;\n"
  },
  {
    "path": "js/actions/video.js",
    "content": "import F8Analytics from \"../F8Analytics\";\nimport { Platform, Share, ActionSheetIOS } from \"react-native\";\n\nimport type { ThunkAction } from \"./types\";\nimport type { Video } from \"../reducers/videos\";\n\nfunction shareVideo(video: Video): ThunkAction {\n  return dispatch => {\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showShareActionSheetWithOptions(\n        {\n          message: video.title,\n          url: video.shareURL\n        },\n        e => {},\n        logShare.bind(null, video.id)\n      );\n    } else {\n      Share.share(\n        {\n          // content\n          title: video.title,\n          message: video.shareURL\n        },\n        {\n          // options\n          dialogTitle: \"Share Link to \" + video.title // droid-only share option\n        }\n      ).then(\n        // callback\n        _ => logShare(video.id, true, null)\n      );\n    }\n  };\n}\n\nfunction logShare(id, completed, activity) {\n  F8Analytics.logEvent(\"Share Video\", 1, { id });\n  // Parse.Analytics.track('share', {\n  //   id,\n  //   completed: completed ? 'yes' : 'no',\n  //   activity: activity || '?'\n  // });\n}\n\nexport { shareVideo };\n"
  },
  {
    "path": "js/common/ActionsOverlay.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { View, Alert } from \"react-native\";\nimport LinearGradient from \"react-native-linear-gradient\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport F8Colors from \"./F8Colors\";\nimport F8Button from \"./F8Button\";\n\n/* config =================================================================== */\n\nconst CONTAINER_HEIGHT = 126,\n  BUTTON_PADDING_H = 17,\n  BUTTON_PADDING_B = 27;\n\n/* =============================================================================\n<ActionsOverlay />\n============================================================================= */\nclass ActionsOverlay extends React.Component {\n  static __cards__;\n  static height = CONTAINER_HEIGHT;\n  static defaultProps = {\n    buttonContainerStyles: {\n      paddingHorizontal: BUTTON_PADDING_H,\n      paddingBottom: BUTTON_PADDING_B\n    },\n    gradientColors: [F8Colors.colorWithAlpha(\"tangaroa\", 0), F8Colors.tangaroa],\n    gradientStart: { x: 0.5, y: 0 },\n    gradientEnd: { x: 0.5, y: 1 }\n  };\n\n  render() {\n    const {\n      children,\n      gradientColors,\n      gradientStart,\n      gradientEnd,\n      buttonContainerStyles\n    } = this.props;\n\n    return (\n      <View\n        style={[styles.container, this.props.style]}\n        pointerEvents=\"box-none\"\n      >\n        <LinearGradient\n          pointerEvents=\"none\"\n          start={gradientStart}\n          end={gradientEnd}\n          style={styles.gradient}\n          colors={gradientColors}\n        />\n        <View\n          pointerEvents=\"box-none\"\n          style={[styles.buttonContainer, buttonContainerStyles]}\n        >\n          {children}\n        </View>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\nconst styles = StyleSheet.create({\n  container: {\n    // position: 'absolute',\n    height: CONTAINER_HEIGHT,\n    // flex:1,\n    justifyContent: \"flex-end\",\n    alignItems: \"center\"\n  },\n  gradient: {\n    position: \"absolute\",\n    top: 0,\n    left: 0,\n    bottom: 0,\n    right: 0\n  },\n  buttonContainer: {\n    flexDirection: \"row\"\n  }\n});\n\n/* Playground Cards\n============================================================================= */\nconst actionsOverlay = ActionsOverlay;\nactionsOverlay.__cards__ = define => {\n  define(\"Default\", () => (\n    <ActionsOverlay>\n      <F8Button\n        theme=\"white\"\n        type=\"round\"\n        icon={require(\"./img/buttons/icon-x.png\")}\n        onPress={_ => Alert.alert(\"round (white) pressed!\")}\n      />\n      <F8Button\n        theme=\"blue\"\n        type=\"round\"\n        style={{ marginLeft: 9 }}\n        icon={require(\"./img/buttons/icon-check.png\")}\n        onPress={_ => Alert.alert(\"round (blue) pressed!\")}\n      />\n    </ActionsOverlay>\n  ));\n\n  define(\"Session Details\", () => (\n    <ActionsOverlay>\n      <F8Button\n        style={{ flex: 1 }}\n        caption=\"Add to my F8\"\n        icon={require(\"./img/buttons/logo-fb.png\")}\n        onPress={_ => Alert.alert(\"button pressed!\")}\n      />\n      <F8Button\n        type=\"round\"\n        style={{ marginLeft: 9 }}\n        icon={require(\"./img/buttons/icon-check.png\")}\n        onPress={_ => Alert.alert(\"round (blue) pressed!\")}\n      />\n    </ActionsOverlay>\n  ));\n};\n\n/* Export\n============================================================================= */\nmodule.exports = actionsOverlay;\n"
  },
  {
    "path": "js/common/Carousel.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport ViewPager from \"./ViewPager\";\nimport { Dimensions } from \"react-native\";\n\ntype Props = {\n  count: number,\n  selectedIndex: number,\n  onSelectedIndexChange?: (index: number) => void,\n  renderCard: (index: number) => ReactElement<any>,\n  style?: any\n};\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width;\n\nclass Carousel extends React.Component {\n  props: Props;\n\n  static CardWidth = WINDOW_WIDTH;\n\n  render() {\n    let cards = [];\n    const { count, selectedIndex, renderCard } = this.props;\n\n    for (let i = 0; i < count; i++) {\n      let content = null;\n      if (Math.abs(i - selectedIndex) < 2) {\n        content = renderCard(i);\n      }\n      cards.push(content);\n    }\n    return (\n      <ViewPager {...this.props} bounces={true}>\n        {cards}\n      </ViewPager>\n    );\n  }\n}\n\nmodule.exports = Carousel;\n"
  },
  {
    "path": "js/common/F8ActionSheet.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { Modal, Animated, View, Alert } from \"react-native\";\nimport LinearGradient from \"react-native-linear-gradient\";\n\nimport { Text } from \"./F8Text\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport F8Colors from \"./F8Colors\";\nimport F8Button from \"./F8Button\";\nimport Hitbox from \"Hitbox\";\n\n/* constants ================================================================ */\nconst GRADIENT_HEIGHT = 220,\n  BUTTON_PADDING_T = 15,\n  BUTTON_PADDING_H = 17,\n  BUTTON_PADDING_B = 18,\n  BUTTON_ANIM_DIST = 50,\n  INTRO_ANIM_DUR = 180,\n  OUTRO_ANIM_DUR = 150;\n\n/**\n* ==============================================================================\n* <F8ActionSheet />\n* ------------------------------------------------------------------------------\n* @param {Array<Object>} actions Action buttons to render { text:str, onPress:fn }\n* @param {function} onCancel 'x' button callback (called after outro animation)\n* @param {?string} title Actions header\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8ActionSheet extends React.Component {\n  constructor() {\n    super();\n\n    this.outro = this.outro.bind(this);\n\n    this.state = {\n      revealed: false,\n      introTransition: new Animated.Value(0)\n    };\n\n    this.animatedContainer = {\n      opacity: this.state.introTransition\n    };\n    this.animatedContent = {\n      marginBottom: this.state.introTransition.interpolate({\n        inputRange: [0, 1],\n        outputRange: [-BUTTON_ANIM_DIST, 0]\n      })\n    };\n\n    Animated.timing(this.state.introTransition, {\n      toValue: 1,\n      duration: INTRO_ANIM_DUR\n    }).start();\n  }\n\n  render() {\n    let actions = (this.props.actions || []).map((action, index) =>\n      this.renderAction(action, index)\n    );\n    let title = this.props.title ? (\n      <Text style={styles.title}>{this.props.title}</Text>\n    ) : null;\n\n    return (\n      <Modal animationType=\"none\" visible={true} transparent={true}>\n        <Animated.View style={[styles.container, this.animatedContainer]}>\n          <Hitbox onPress={_ => this.outro(this.props.onCancel)} />\n          <Animated.View style={this.animatedContent} pointerEvents=\"box-none\">\n            <View pointerEvents=\"none\">\n              <LinearGradient\n                start={{ x: 0.5, y: 0 }}\n                end={{ x: 0.5, y: 1 }}\n                style={styles.gradient}\n                colors={[\n                  F8Colors.colorWithAlpha(\"tangaroa\", 0),\n                  F8Colors.colorWithAlpha(\"tangaroa\", 0.8)\n                ]}\n              />\n            </View>\n            <View style={styles.buttonGroup}>\n              {title}\n              {actions}\n              {this.renderCancel()}\n            </View>\n          </Animated.View>\n        </Animated.View>\n      </Modal>\n    );\n  }\n\n  renderAction(obj, i) {\n    return (\n      <F8Button\n        key={`F8ASAB-${obj.text}`}\n        style={styles.actionBtn}\n        caption={obj.text}\n        onPress={() => this.outro(obj.onPress)}\n      />\n    );\n  }\n\n  renderCancel() {\n    return (\n      <F8Button\n        key=\"F8ASCB\"\n        theme=\"white\"\n        type=\"round\"\n        style={styles.cancelBtn}\n        icon={require(\"./img/buttons/icon-x.png\")}\n        onPress={_ => this.outro(this.props.onCancel)}\n      />\n    );\n  }\n\n  outro(cb) {\n    Animated.timing(this.state.introTransition, {\n      toValue: 0,\n      duration: OUTRO_ANIM_DUR\n    }).start(cb);\n  }\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  container: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    flex: 1,\n    justifyContent: \"flex-end\",\n    backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.5)\n  },\n  gradient: {\n    height: GRADIENT_HEIGHT\n  },\n  buttonGroup: {\n    paddingTop: BUTTON_PADDING_T,\n    paddingHorizontal: BUTTON_PADDING_H,\n    paddingBottom: BUTTON_PADDING_B,\n    backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.8)\n  },\n  actionBtn: {\n    marginBottom: 10\n  },\n  cancelBtn: {\n    alignSelf: \"center\",\n    marginTop: 5\n  },\n  title: {\n    color: F8Colors.white,\n    fontSize: 18,\n    textAlign: \"center\",\n    padding: 20\n  }\n});\n\n/* playground cards ========================================================= */\nconst actionSheet = F8ActionSheet;\nactionSheet.__cards__ = define => {\n  const exampleActions = [\n    { text: \"Action 1\", onPress: () => Alert.alert(\"Do action 1!\") },\n    { text: \"Action 2\", onPress: () => Alert.alert(\"Do action 2!\") }\n  ];\n\n  define(\"Default\", () => (\n    <F8ActionSheet\n      actions={exampleActions}\n      title=\"This is a title!\"\n      onCancel={() => Alert.alert(\"Cancel\")}\n    />\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = actionSheet;\n"
  },
  {
    "path": "js/common/F8BackgroundRepeat.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { View, Image } from \"react-native\";\nimport resolveAssetSource from \"resolveAssetSource\";\n\n/**\n* ==============================================================================\n* <F8BackgroundRepeat />\n* ------------------------------------------------------------------------------\n* @param {number} source ReactNative asset source\n* @param {number} width repeating container width\n* @param {number} height repeating container height\n* @return {ReactElement}\n* ==============================================================================\n*/\nclass F8BackgroundRepeat extends React.Component {\n  static __cards__;\n\n  render() {\n    const { source, width, height } = this.props;\n    const img = resolveAssetSource(source);\n    const content = [];\n\n    const numHorizontal = Math.ceil(width / img.width);\n    const numVertical = Math.ceil(height / img.height);\n\n    for (let i = 0; i < numVertical; i++) {\n      content.push(this.renderRow(numHorizontal, i));\n    }\n\n    return (\n      <View\n        style={[\n          { width, height, zIndex: 0, overflow: \"hidden\" },\n          this.props.style\n        ]}\n      >\n        {content}\n      </View>\n    );\n  }\n\n  renderRow(colsInRow: number, idx: number) {\n    const cols = [];\n    for (let i = 0; i < colsInRow; i++) {\n      cols.push(this.renderImage(i));\n    }\n    return (\n      <View key={`bgRptRow${idx}`} style={{ flexDirection: \"row\" }}>\n        {cols}\n      </View>\n    );\n  }\n\n  renderImage(idx: number) {\n    return <Image key={`bgRptImg${idx}`} source={this.props.source} />;\n  }\n}\n\n/* Playground Cards ========================================================= */\n\nconst backgroundRepeat = F8BackgroundRepeat;\nbackgroundRepeat.__cards__ = define => {\n  define(\"Back Buttons\", _ => (\n    <F8BackgroundRepeat\n      style={{ backgroundColor: \"black\" }}\n      width={350}\n      height={80}\n      source={require(\"./img/header/back.png\")}\n    />\n  ));\n  define(\"F8 Logos\", _ => (\n    <F8BackgroundRepeat\n      style={{ backgroundColor: \"black\" }}\n      width={250}\n      height={200}\n      source={require(\"./img/webview/logo.png\")}\n    />\n  ));\n  define(\"Dot Pattern\", _ => (\n    <F8BackgroundRepeat\n      style={{ backgroundColor: \"white\" }}\n      width={300}\n      height={300}\n      source={require(\"./img/pattern-dots.png\")}\n    />\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = backgroundRepeat;\n"
  },
  {
    "path": "js/common/F8Button.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport F8Fonts from \"./F8Fonts\";\nimport { View, StyleSheet, TouchableOpacity, Image, Alert } from \"react-native\";\nimport { Text } from \"./F8Text\";\n\n/* constants ================================================================ */\n\nconst BUTTON_HEIGHT = 52,\n  BUTTON_HEIGHT_SM = 32;\n\n/* <F8Button />\n============================================================================= */\n\nclass F8Button extends React.Component {\n  props: {\n    theme:\n      | \"pink\"\n      | \"blue\"\n      | \"yellow\"\n      | \"fb\"\n      | \"white\"\n      | \"bordered\"\n      | \"bordered-pink\"\n      | \"disabled\",\n    type: \"default\" | \"round\" | \"small\",\n    opacity: number,\n    icon?: number,\n    caption?: string,\n    style?: any,\n    fontSize?: number,\n    // buttonColor?: string;\n    // contentColor?: string;\n    onPress: () => mixed\n  };\n\n  static defaultProps = {\n    opacity: 1,\n    theme: \"pink\"\n  };\n\n  static height = BUTTON_HEIGHT;\n\n  render() {\n    const { icon, fontSize, opacity } = this.props;\n    const caption = this.props.caption && this.props.caption.toUpperCase();\n    const { buttonTheme, iconTheme, captionTheme } = this.getTheme();\n    const { containerType, buttonType, iconType, captionType } = this.getType();\n\n    let iconImage;\n    if (icon) {\n      iconImage = (\n        <Image source={icon} style={[styles.icon, iconTheme, iconType]} />\n      );\n    }\n\n    let fontSizeOverride;\n    if (fontSize) {\n      fontSizeOverride = { fontSize };\n    }\n\n    const content = (\n      <View style={[styles.button, buttonTheme, buttonType, { opacity }]}>\n        {iconImage}\n        <Text\n          style={[styles.caption, captionTheme, captionType, fontSizeOverride]}\n        >\n          {caption}\n        </Text>\n      </View>\n    );\n\n    if (this.props.onPress) {\n      return (\n        <TouchableOpacity\n          accessibilityTraits=\"button\"\n          onPress={this.props.onPress}\n          activeOpacity={0.5}\n          style={[styles.container, containerType, this.props.style]}\n        >\n          {content}\n        </TouchableOpacity>\n      );\n    } else {\n      return (\n        <View style={[styles.container, containerType, this.props.style]}>\n          {content}\n        </View>\n      );\n    }\n  }\n\n  getTheme() {\n    const { theme } = this.props;\n    let buttonTheme, iconTheme, captionTheme;\n    if (theme === \"yellow\") {\n      buttonTheme = { backgroundColor: F8Colors.yellow };\n      iconTheme = { tintColor: F8Colors.pink };\n      captionTheme = { color: F8Colors.pink };\n    } else if (theme === \"blue\") {\n      buttonTheme = { backgroundColor: F8Colors.blue };\n      iconTheme = { tintColor: F8Colors.white };\n      captionTheme = { color: F8Colors.white };\n    } else if (theme === \"fb\") {\n      buttonTheme = { backgroundColor: F8Colors.facebookBlue };\n      iconTheme = { tintColor: F8Colors.white };\n      captionTheme = { color: F8Colors.white };\n    } else if (theme === \"white\") {\n      buttonTheme = { backgroundColor: F8Colors.white };\n      iconTheme = { tintColor: F8Colors.pink };\n      captionTheme = { color: F8Colors.pink };\n    } else if (theme === \"bordered\") {\n      buttonTheme = {\n        backgroundColor: \"transparent\",\n        borderWidth: 1,\n        borderColor: F8Colors.tangaroa\n      };\n      iconTheme = { tintColor: F8Colors.tangaroa };\n      captionTheme = { color: F8Colors.tangaroa };\n    } else if (theme === \"bordered-pink\") {\n      buttonTheme = {\n        backgroundColor: \"transparent\",\n        borderWidth: 1,\n        borderColor: F8Colors.pink\n      };\n      iconTheme = { tintColor: F8Colors.pink };\n      captionTheme = { color: F8Colors.pink };\n    } else if (theme === \"maps\") {\n      buttonTheme = {\n        backgroundColor: \"transparent\",\n        borderWidth: 1,\n        borderColor: F8Colors.purple\n      };\n      iconTheme = { tintColor: F8Colors.tangaroa };\n      captionTheme = { color: F8Colors.tangaroa };\n    } else if (theme === \"mapsInactive\") {\n      buttonTheme = {\n        backgroundColor: \"transparent\",\n        borderWidth: 1,\n        borderColor: \"transparent\"\n      };\n      iconTheme = { tintColor: F8Colors.tangaroa };\n      captionTheme = { color: F8Colors.tangaroa };\n    } else if (theme === \"disabled\") {\n      buttonTheme = { backgroundColor: F8Colors.blueBayoux };\n      iconTheme = { tintColor: F8Colors.white, opacity: 0.5 };\n      captionTheme = { color: F8Colors.white, opacity: 0.5 };\n    } else if (theme === \"transparent\") {\n      buttonTheme = { backgroundColor: \"transparent\" };\n      iconTheme = { tintColor: F8Colors.tangaroa };\n      captionTheme = { color: F8Colors.tangaroa };\n    } else {\n      // pink/white is default\n      buttonTheme = { backgroundColor: F8Colors.pink };\n      iconTheme = { tintColor: \"white\" };\n      captionTheme = { color: \"white\" };\n    }\n\n    return { buttonTheme, iconTheme, captionTheme };\n  }\n\n  getType() {\n    const { type } = this.props;\n    let containerType, buttonType, iconType, captionType;\n\n    if (type === \"round\") {\n      buttonType = { width: BUTTON_HEIGHT, paddingHorizontal: 0 };\n      iconType = { marginRight: 0 };\n      captionType = { fontSize: 13 };\n    } else if (type === \"small\") {\n      containerType = { height: BUTTON_HEIGHT_SM };\n      buttonType = { paddingHorizontal: 15 };\n      iconType = { marginRight: 0 };\n      captionType = { fontSize: 13 };\n    } else {\n      // defaults\n    }\n\n    return { containerType, buttonType, iconType, captionType };\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    height: BUTTON_HEIGHT\n  },\n  button: {\n    flex: 1,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    paddingHorizontal: 30,\n    borderRadius: BUTTON_HEIGHT / 2\n  },\n  buttonRound: {\n    width: BUTTON_HEIGHT,\n    paddingHorizontal: 0\n  },\n  icon: {\n    marginRight: 12\n  },\n  caption: {\n    fontFamily: F8Fonts.button,\n    fontSize: 15,\n    textAlign: \"center\"\n  }\n});\n\n/* Playground Cards\n============================================================================= */\n\nconst Button = F8Button;\nButton.__cards__ = define => {\n  define(\"default (pink)\", () => (\n    <F8Button\n      caption=\"default (pink)\"\n      onPress={_ => Alert.alert(\"default (pink) pressed!\")}\n    />\n  ));\n\n  define(\"blue\", () => (\n    <F8Button\n      theme=\"blue\"\n      caption=\"blue\"\n      onPress={_ => Alert.alert(\"blue pressed!\")}\n    />\n  ));\n\n  define(\"yellow (w/ icon)\", () => (\n    <F8Button\n      theme=\"yellow\"\n      caption=\"yellow (icon)\"\n      icon={require(\"./img/buttons/logo-fb.png\")}\n      onPress={_ => Alert.alert(\"yellow (icon) pressed!\")}\n    />\n  ));\n\n  define(\"fb\", () => (\n    <F8Button\n      theme=\"fb\"\n      caption=\"fb\"\n      icon={require(\"./img/buttons/logo-fb.png\")}\n      onPress={_ => Alert.alert(\"fb pressed!\")}\n    />\n  ));\n\n  define(\"white\", () => (\n    <F8Button\n      theme=\"white\"\n      caption=\"white\"\n      onPress={_ => Alert.alert(\"white pressed!\")}\n    />\n  ));\n\n  define(\"bordered\", () => (\n    <F8Button\n      theme=\"bordered\"\n      caption=\"bordered\"\n      onPress={_ => Alert.alert(\"bordered pressed!\")}\n    />\n  ));\n\n  define(\"bordered-pink\", () => (\n    <F8Button\n      theme=\"bordered-pink\"\n      caption=\"bordered-pink\"\n      onPress={_ => Alert.alert(\"bordered-pink pressed!\")}\n    />\n  ));\n\n  define(\"round (caption)\", () => (\n    <F8Button\n      type=\"round\"\n      caption=\"My F8\"\n      onPress={_ => Alert.alert(\"round (caption) pressed!\")}\n    />\n  ));\n\n  define(\"round (white)\", () => (\n    <F8Button\n      theme=\"white\"\n      type=\"round\"\n      icon={require(\"./img/buttons/icon-x.png\")}\n      onPress={_ => Alert.alert(\"round (white) pressed!\")}\n    />\n  ));\n\n  define(\"round (blue)\", () => (\n    <F8Button\n      theme=\"blue\"\n      type=\"round\"\n      icon={require(\"./img/buttons/icon-check.png\")}\n      onPress={_ => Alert.alert(\"round (blue) pressed!\")}\n    />\n  ));\n\n  define(\"fixed width\", () => (\n    <F8Button\n      theme=\"bordered\"\n      caption=\"Fixed 250 width\"\n      style={{ width: 250 }}\n      onPress={_ => Alert.alert(\"round (blue) pressed!\")}\n    />\n  ));\n\n  define(\"small (blue)\", () => (\n    <F8Button\n      theme=\"maps\"\n      type=\"small\"\n      caption=\"Small\"\n      onPress={_ => Alert.alert(\"small (maps) pressed!\")}\n    />\n  ));\n};\n\n/* Exports\n============================================================================= */\nmodule.exports = Button;\n"
  },
  {
    "path": "js/common/F8Colors.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\n\"use strict\";\n\n/* Color Definitions\n============================================================================= */\n\nconst NAMED_COLORS = {\n  // grayscale (light to dark)\n  white: \"rgba(255, 255, 255, 1)\",\n  bianca: \"rgba(251, 249, 240, 1)\",\n  timberwolf: \"rgba(218, 216, 210, 1)\",\n  magnesium: \"rgba(178, 178, 178, 1)\",\n  black: \"rgba(3, 3, 3, 1)\",\n\n  // blues (light to dark)\n  iceberg: \"rgba(216, 240, 246, 1)\",\n  coolGray: \"rgba(136, 145, 181, 1)\",\n  blueBayoux: \"rgba(101, 113, 135, 1)\",\n  facebookBlue: \"rgba(66, 103, 178, 1)\",\n  blue: \"rgba(29, 86, 251, 1)\",\n  palatinateBlue: \"rgba(24, 76, 223, 1)\",\n  persianBlue: \"rgba(23, 68, 200, 1)\",\n  sapphire: \"rgba(10, 42, 102, 1)\",\n  sapphire2: \"rgba(18, 36, 108, 1)\",\n  tangaroa: \"rgba(1, 23, 65, 1)\",\n  blueCharcoal: \"rgba(1, 10, 28, 1)\",\n\n  // the rest\n  yellow: \"rgba(246, 253, 55, 1)\",\n  green: \"rgba(106, 246, 162, 1)\",\n  turquoise: \"rgba(0, 205, 223, 1)\",\n  purple: \"rgba(144, 63, 199, 1)\",\n  pink: \"rgba(245, 64, 199, 1)\",\n  darkPink: \"rgba(200, 40, 159, 1)\",\n  orange: \"rgba(247, 144, 77, 1)\",\n  salmon: \"rgba(243, 91, 89, 1)\"\n};\n\nconst THEME_COLORS = {\n  // pass through for use with colorWithAlpha\n  ...NAMED_COLORS,\n\n  // alias the named colors by use-case\n  actionText: NAMED_COLORS.blue,\n  lightBackground: NAMED_COLORS.bianca,\n  darkBackground: NAMED_COLORS.blueCharcoal,\n  darkText: NAMED_COLORS.blueCharcoal,\n  cellBorder: NAMED_COLORS.blueCharcoal,\n  lightText: NAMED_COLORS.blueBayoux,\n\n  // legacy\n  inactiveText: \"#9B9B9B\"\n};\n\nconst LOCATION_COLORS = {\n  \"220A\": NAMED_COLORS.sapphire2,\n  \"220B\": NAMED_COLORS.purple,\n  \"220C\": NAMED_COLORS.blue,\n  \"210F\": NAMED_COLORS.turquoise,\n  \"210G\": NAMED_COLORS.turquoise,\n  LL20: NAMED_COLORS.green,\n  REGISTRATION: NAMED_COLORS.tangaroa,\n  REGISTRATIONDESK: NAMED_COLORS.tangaroa,\n  FESTIVALHALL: NAMED_COLORS.sapphire2,\n  CITYNATIONALCIVIC: NAMED_COLORS.pink,\n\n  A: NAMED_COLORS.sapphire2,\n  B: NAMED_COLORS.purple,\n  C: NAMED_COLORS.blue,\n  F: NAMED_COLORS.turquoise,\n  G: NAMED_COLORS.turquoise,\n  LL: NAMED_COLORS.green\n};\n\n/* Exports\n============================================================================= */\n\nmodule.exports = {\n  ...THEME_COLORS, // pass through all theme colors (named and by-purpose)\n\n  colorWithAlpha(name: string = \"blue\", opacity: number = 1) {\n    if (!THEME_COLORS[name]) {\n      name = \"blue\";\n    }\n    return THEME_COLORS[name].split(\", 1)\").join(`, ${opacity})`);\n  },\n\n  colorForLocation(location: ?string): string {\n    if (!location) {\n      return NAMED_COLORS.tangaroa;\n    }\n    let color = LOCATION_COLORS[location.replace(/ /g, \"\").toUpperCase()];\n    if (!color) {\n      color = NAMED_COLORS.tangaroa;\n    }\n    return color;\n  },\n\n  colorForTopic(count: number, index: number): string {\n    const hue = Math.round(360 * index / (count + 1));\n    return `hsl(${hue}, 74%, 65%)`;\n  }\n};\n"
  },
  {
    "path": "js/common/F8DrawerLayout.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { DrawerLayoutAndroid } from \"react-native\";\n\nclass F8DrawerLayout extends React.Component {\n  _drawer: ?DrawerLayoutAndroid;\n\n  constructor(props: any, context: any) {\n    super(props, context);\n\n    (this: any).openDrawer = this.openDrawer.bind(this);\n    (this: any).closeDrawer = this.closeDrawer.bind(this);\n    (this: any).onDrawerOpen = this.onDrawerOpen.bind(this);\n    (this: any).onDrawerClose = this.onDrawerClose.bind(this);\n    (this: any).handleBackButton = this.handleBackButton.bind(this);\n  }\n\n  render() {\n    const { drawerPosition, ...props } = this.props;\n    const { Right, Left } = DrawerLayoutAndroid.positions;\n    return (\n      <DrawerLayoutAndroid\n        ref={drawer => {\n          this._drawer = drawer;\n        }}\n        {...props}\n        drawerPosition={drawerPosition === \"right\" ? Right : Left}\n        onDrawerOpen={this.onDrawerOpen}\n        onDrawerClose={this.onDrawerClose}\n      />\n    );\n  }\n\n  componentWillUnmount() {\n    this.context.removeBackButtonListener(this.handleBackButton);\n    this._drawer = null;\n  }\n\n  handleBackButton(): boolean {\n    this.closeDrawer();\n    return true;\n  }\n\n  onDrawerOpen() {\n    this.context.addBackButtonListener(this.handleBackButton);\n    this.props.onDrawerOpen && this.props.onDrawerOpen();\n  }\n\n  onDrawerClose() {\n    this.context.removeBackButtonListener(this.handleBackButton);\n    this.props.onDrawerClose && this.props.onDrawerClose();\n  }\n\n  closeDrawer() {\n    this._drawer && this._drawer.closeDrawer();\n  }\n\n  openDrawer() {\n    this._drawer && this._drawer.openDrawer();\n  }\n}\n\nF8DrawerLayout.contextTypes = {\n  addBackButtonListener: React.PropTypes.func,\n  removeBackButtonListener: React.PropTypes.func\n};\n\nmodule.exports = F8DrawerLayout;\n"
  },
  {
    "path": "js/common/F8Fonts.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\n\"use strict\";\n\nimport { Platform, Dimensions } from \"react-native\";\n\nconst DEVICE_SCALE = Dimensions.get(\"window\").width / 375;\n\nconst DEFAULT_FONT = \"helvetica\";\nconst SECONDARY_FONT = Platform.OS === \"android\" ? \"basis\" : \"helvetica\";\n\n/* utils ==================================================================== */\n\n// get font name and weight\nfunction fontWithWeight(\n  family: string = DEFAULT_FONT,\n  weight: string = \"regular\"\n): string {\n  return family;\n}\n\nfunction normalize(size: number): number {\n  return Math.round(DEVICE_SCALE * size);\n}\n\n// attempt to normalize x-platform line heights\nfunction lineHeight(\n  val: number = 1,\n  scale: number = 1,\n  normalized: boolean = true\n): number {\n  let adjusted = normalized ? normalize(val) : val;\n  return Math.round(Platform.OS === \"android\" ? adjusted * scale : adjusted);\n}\n\n/* export =================================================================== */\n\nexport default {\n  default: DEFAULT_FONT,\n  helvetica: DEFAULT_FONT,\n  basis: SECONDARY_FONT,\n  h1: DEFAULT_FONT,\n  h2: DEFAULT_FONT,\n  h3: DEFAULT_FONT,\n  h4: DEFAULT_FONT,\n  p: DEFAULT_FONT,\n  button: DEFAULT_FONT,\n\n  fontWithWeight,\n  lineHeight,\n  normalize\n};\n"
  },
  {
    "path": "js/common/F8Header.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport {\n  Dimensions,\n  View,\n  Image,\n  ToolbarAndroid,\n  Platform,\n  TouchableOpacity,\n  Alert\n} from \"react-native\";\nimport { Text, HeaderTitle } from \"./F8Text\";\nimport F8Colors from \"./F8Colors\";\nimport F8Fonts from \"./F8Fonts\";\nimport StyleSheet from \"./F8StyleSheet\";\n\n/* Config\n============================================================================= */\n\nlet STATUS_BAR_HEIGHT = Platform.OS === \"ios\" ? 20 : 25;\nif (Platform.OS === \"android\" && Platform.Version && Platform.Version < 21) {\n  STATUS_BAR_HEIGHT = 0;\n}\nconst HEADER_HEIGHT =\n  Platform.OS === \"ios\" ? 45 + STATUS_BAR_HEIGHT : 60 + STATUS_BAR_HEIGHT;\nconst SCREEN_WIDTH = Dimensions.get(\"window\").width;\nconst IOS_ITEM_TEXT_SIZE = SCREEN_WIDTH < 375 ? 10 : 13;\n\nconst FAVORITE_ICON_WIDTH = 37,\n  FAVORITE_ICON_HEIGHT = 31;\n\nexport type Layout =\n  | \"default\" // Use platform defaults (icon on Android, text on iOS)\n  | \"icon\" // Always use icon\n  | \"title\"; // Always use title\n\nexport type Item = {\n  title?: string,\n  icon?: number,\n  layout?: Layout,\n  onPress?: () => void\n};\n\nexport type Props = {\n  title?: string,\n  leftItem?: Item,\n  rightItem?: Item,\n  extraItems?: Array<Item>,\n  style?: any,\n  children?: any\n};\n\n/* =============================================================================\n<F8Header /> (When Platform.os is Android)\n--------------------------------------------------------------------------------\nToolbarAndroid header\n\n============================================================================= */\n\nclass F8HeaderAndroid extends React.Component {\n  static height: number;\n  props: Props;\n\n  static defaultProps = {\n    backgroundColor: F8Colors.blue,\n    titleColor: F8Colors.yellow,\n    itemsColor: F8Colors.white\n  };\n\n  constructor() {\n    super();\n\n    this.limitActionSelection = false;\n  }\n\n  render() {\n    const {\n      navItem,\n      leftItem,\n      rightItem,\n      extraItems,\n      backgroundColor,\n      titleColor\n    } = this.props;\n\n    let actions = [];\n    if (leftItem) {\n      const { title, icon, layout } = leftItem;\n      actions.push({\n        icon: layout !== \"title\" ? icon : undefined,\n        title: title,\n        show: \"always\"\n      });\n    }\n    if (rightItem) {\n      const { title, icon, layout } = rightItem;\n      actions.push({\n        icon: layout !== \"title\" ? icon : undefined,\n        title: title,\n        show: \"always\"\n      });\n    }\n    if (extraItems) {\n      actions = actions.concat(\n        extraItems.map(item => ({\n          title: item.title,\n          show: \"never\"\n        }))\n      );\n    }\n\n    let content;\n    if (React.Children.count(this.props.children) > 0) {\n      content = (\n        <View collapsable={false} style={{ flex: 1 }}>\n          {this.props.children}\n        </View>\n      );\n    } else {\n      content = (\n        <View collapsable={false} style={{ flex: 1, justifyContent: \"center\" }}>\n          <HeaderTitle numberOfLines={1} style={{ color: titleColor }}>\n            {this.props.title}\n          </HeaderTitle>\n        </View>\n      );\n    }\n\n    return (\n      <View style={[styles.header, { backgroundColor }, this.props.style]}>\n        <ToolbarAndroid\n          navIcon={navItem && navItem.icon}\n          onIconClicked={navItem && navItem.onPress}\n          title={this.props.title}\n          titleColor={titleColor}\n          subtitleColor={titleColor}\n          actions={actions}\n          onActionSelected={this.handleActionSelected.bind(this)}\n          style={styles.toolbar}\n        >\n          {content}\n        </ToolbarAndroid>\n        <Text style={{ height: 0, opacity: 0 }}>{actions.length || 0}</Text>\n      </View>\n    );\n  }\n\n  handleActionSelected(position: number) {\n    if (this.limitActionSelection) {\n      return;\n    }\n    let items = this.props.extraItems || [];\n    if (this.props.rightItem) {\n      items = [this.props.rightItem, ...items];\n    }\n    if (this.props.leftItem) {\n      items = [this.props.leftItem, ...items];\n    }\n    const item = items[position];\n    item && item.onPress && item.onPress();\n    this.limitActionSelection = true;\n    setTimeout(() => {\n      this.limitActionSelection = false;\n    }, 1000);\n  }\n}\n\n/* =============================================================================\n<F8Header /> (When Platform.os is iOS)\n--------------------------------------------------------------------------------\nView header\n\n============================================================================= */\n\nclass F8HeaderIOS extends React.Component {\n  static height: number;\n  props: Props;\n\n  static defaultProps = {\n    backgroundColor: F8Colors.blue,\n    titleColor: F8Colors.yellow,\n    itemsColor: F8Colors.white\n  };\n\n  render() {\n    const {\n      navItem,\n      leftItem,\n      title,\n      rightItem,\n      backgroundColor,\n      titleColor,\n      itemsColor\n    } = this.props;\n\n    let left;\n    if (navItem) {\n      if (navItem.back) {\n        navItem.icon = require(\"./img/header/back.png\");\n      }\n      left = (\n        <ItemWrapperIOS\n          color={itemsColor}\n          item={{ ...navItem, layout: \"icon\" }}\n        />\n      );\n    } else {\n      left = <ItemWrapperIOS color={itemsColor} item={leftItem} />;\n    }\n\n    const content =\n      React.Children.count(this.props.children) === 0 ? (\n        <HeaderTitle numberOfLines={1} style={{ color: titleColor }}>\n          {title}\n        </HeaderTitle>\n      ) : (\n        this.props.children\n      );\n\n    const right = <ItemWrapperIOS color={itemsColor} item={rightItem} />;\n\n    return (\n      <View style={[styles.header, { backgroundColor }, this.props.style]}>\n        <View style={styles.leftItem}>{left}</View>\n        <View\n          accessible={true}\n          accessibilityLabel={title}\n          accessibilityTraits=\"header\"\n          style={styles.centerItem}\n        >\n          {content}\n        </View>\n        <View style={styles.rightItem}>{right}</View>\n      </View>\n    );\n  }\n}\n\n/* =============================================================================\n<ItemWrapperIOS />\n--------------------------------------------------------------------------------\nItem wrapper\n\n============================================================================= */\n\nclass ItemWrapperIOS extends React.Component {\n  props: {\n    item: Item,\n    color: string\n  };\n\n  render() {\n    const { item, color } = this.props;\n    if (!item) {\n      return null;\n    }\n\n    let content;\n    const { title, icon, layout, onPress } = item;\n\n    if (layout !== \"icon\" && title) {\n      content = (\n        <Text style={[styles.itemText, { color }]}>{title.toUpperCase()}</Text>\n      );\n    } else if (icon) {\n      content = <Image source={icon} style={{ tintColor: color }} />;\n    }\n\n    return (\n      <TouchableOpacity\n        accessibilityLabel={title}\n        accessibilityTraits=\"button\"\n        onPress={onPress}\n        style={styles.itemWrapper}\n      >\n        {content}\n      </TouchableOpacity>\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  toolbar: {\n    android: {\n      // backgroundColor: F8Colors.background,\n      height: HEADER_HEIGHT - STATUS_BAR_HEIGHT\n    }\n  },\n  header: {\n    android: {\n      paddingTop: STATUS_BAR_HEIGHT\n    },\n    ios: {\n      backgroundColor: \"transparent\",\n      paddingTop: STATUS_BAR_HEIGHT,\n      height: HEADER_HEIGHT,\n      flexDirection: \"row\",\n      justifyContent: \"space-between\",\n      alignItems: \"center\"\n    }\n  },\n  titleTextDivider: {\n    android: {\n      position: \"absolute\",\n      height: 1,\n      bottom: 0,\n      backgroundColor: F8Colors.accent\n    }\n  },\n  leftItem: {\n    flex: 1,\n    alignItems: \"flex-start\"\n  },\n  centerItem: {\n    flex: 2,\n    alignItems: \"center\"\n  },\n  rightItem: {\n    flex: 1,\n    alignItems: \"flex-end\"\n  },\n  itemWrapper: {\n    padding: 11\n  },\n  itemText: {\n    fontFamily: F8Fonts.helvetica,\n    fontSize: IOS_ITEM_TEXT_SIZE,\n    color: F8Colors.highContrast\n  },\n  favoriteIcon: {\n    android: {\n      position: \"absolute\",\n      width: FAVORITE_ICON_WIDTH,\n      height: FAVORITE_ICON_HEIGHT,\n      top: HEADER_HEIGHT / 2 - FAVORITE_ICON_HEIGHT / 2 + STATUS_BAR_HEIGHT / 2,\n      right: 0\n    }\n  }\n});\n\n/* Playground Cards\n============================================================================= */\n\nconst Header = Platform.OS === \"ios\" ? F8HeaderIOS : F8HeaderAndroid;\nHeader.height = HEADER_HEIGHT;\n// $FlowFixMe\nHeader.__cards__ = define => {\n  const navItem = {\n    back: true,\n    onPress: () => Alert.alert(\"Menu button pressed!\")\n  };\n  const favoritesItem = {\n    title: \"Favorites\",\n    icon: require(\"./img/header/star.png\"),\n    onPress: () => Alert.alert(\"Favorites button pressed!\")\n  };\n  const topicsItem = {\n    title: \"Topics\",\n    icon: require(\"./img/header/filter.png\"),\n    onPress: () => Alert.alert(\"Topics button pressed!\")\n  };\n\n  define(\"Simple\", () => <Header title=\"Hello, world\" type=\"underline\" />);\n  define(\"Simple w/ Nav\", () => (\n    <Header title=\"Hello, world\" navItem={navItem} />\n  ));\n  define(\"With items\", () => (\n    <Header\n      title=\"Default\"\n      navItem={navItem}\n      leftItem={favoritesItem}\n      rightItem={topicsItem}\n    />\n  ));\n  define(\"Forcing icons\", () => (\n    <Header\n      title=\"Forcing icons\"\n      leftItem={{ ...favoritesItem, layout: \"icon\" }}\n      rightItem={{ ...topicsItem, layout: \"icon\" }}\n    />\n  ));\n  define(\"Forcing title\", () => (\n    <Header\n      title=\"Forcing title\"\n      leftItem={{ ...favoritesItem, layout: \"title\" }}\n      rightItem={{ ...topicsItem, layout: \"title\" }}\n    />\n  ));\n  define(\"With content\", () => {\n    const alignmentStyles =\n      Platform.OS === \"android\" ? null : { textAlign: \"center\" };\n    return (\n      <Header navItem={navItem}>\n        <Text\n          style={[{ color: F8Colors.bianca, fontSize: 13 }, alignmentStyles]}\n        >\n          Wed 9/23\n          {\"\\n\"}\n          12:00\n        </Text>\n      </Header>\n    );\n  });\n  define(\"With Background\", () => (\n    <Header\n      title=\"With Background\"\n      leftItem={favoritesItem}\n      rightItem={topicsItem}\n      style={{ backgroundColor: F8Colors.pink }}\n    />\n  ));\n  define(\"With light background\", () => (\n    <Header\n      title=\"Light Background\"\n      leftItem={favoritesItem}\n      rightItem={topicsItem}\n      backgroundColor={F8Colors.yellow}\n      titleColor={F8Colors.purple}\n      itemsColor={F8Colors.black}\n    />\n  ));\n};\n\n/* Export\n============================================================================= */\nmodule.exports = Header;\n"
  },
  {
    "path": "js/common/F8Linking.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport { Linking } from \"react-native\";\n\nconst WHITELISTED_URL_SCHEMES = [\n  \"https:\",\n  \"http:\",\n  \"mailto:\",\n  \"comgooglemaps-x-callback:\"\n];\nconst ERR_NOT_LISTED = \"F8Linking: URL does not match whitelisted schemes\";\n\nfunction allowed(source: string) {\n  return !!WHITELISTED_URL_SCHEMES.find(p => source.indexOf(p) === 0);\n}\n\nexport default class F8Linking {\n  static async openURL(source: string) {\n    if (!allowed(source)) {\n      throw new Error(ERR_NOT_LISTED);\n    }\n    return Linking.openURL(source);\n  }\n  static async canOpenURL(source: string) {\n    if (!allowed(source)) {\n      return false;\n    }\n    return Linking.canOpenURL(source);\n  }\n}\n"
  },
  {
    "path": "js/common/F8Modal.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { Modal, View, StyleSheet } from \"react-native\";\nimport LinearGradient from \"react-native-linear-gradient\";\n\n/* Constants\n============================================================================= */\n\nconst MODAL_PADDING_H = 10,\n  MODAL_BORDER_RADIUS = 4,\n  FOOTER_GRADIENT_HEIGHT = 126;\n\n/* <F8Modal />\n============================================================================= */\n\nclass F8Modal extends React.Component {\n  static defaultProps = {\n    transparent: true,\n    animationType: \"fade\"\n  };\n\n  render() {\n    return (\n      <Modal style={[styles.background, this.props.style]} {...this.props}>\n        <View style={styles.container}>\n          {this.props.bottomGradient && this.props.bottomGradient.length === 2\n            ? this.renderBottomGradient()\n            : null}\n          {this.props.renderContent ? this.renderContent() : null}\n          {this.props.renderFooter ? this.renderFooter() : null}\n        </View>\n      </Modal>\n    );\n  }\n\n  renderContent() {\n    return <View style={styles.card}>{this.props.renderContent()}</View>;\n  }\n  renderFooter() {\n    return <View style={styles.footer}>{this.props.renderFooter()}</View>;\n  }\n  renderBottomGradient() {\n    return (\n      <LinearGradient\n        pointerEvents=\"none\"\n        start={{ x: 0.5, y: 0 }}\n        end={{ x: 0.5, y: 1 }}\n        style={styles.bottomGradient}\n        colors={this.props.bottomGradient}\n      />\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.8),\n    justifyContent: \"center\",\n    alignItems: \"center\",\n    paddingHorizontal: MODAL_PADDING_H\n  },\n  card: {\n    backgroundColor: F8Colors.white,\n    borderRadius: MODAL_BORDER_RADIUS\n  },\n  footer: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0,\n    alignItems: \"center\"\n  },\n  bottomGradient: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0,\n    height: FOOTER_GRADIENT_HEIGHT\n  }\n});\n\n/* Exports\n============================================================================= */\nmodule.exports = F8Modal;\n"
  },
  {
    "path": "js/common/F8PageControl.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, View } from \"react-native\";\n\nconst PropTypes = React.PropTypes;\n\nconst F8PageControl = React.createClass({\n  propTypes: {\n    style: View.propTypes.style,\n    count: PropTypes.number.isRequired,\n    selectedIndex: PropTypes.number.isRequired\n  },\n\n  render: function() {\n    const images = [];\n    for (let i = 0; i < this.props.count; i++) {\n      const isSelected = this.props.selectedIndex === i;\n      images.push(<Circle key={i} isSelected={isSelected} />);\n    }\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <View style={styles.innerContainer}>{images}</View>\n      </View>\n    );\n  }\n});\n\nconst Circle = React.createClass({\n  render: function() {\n    const extraStyle = this.props.isSelected ? styles.full : styles.empty;\n    return <View style={[styles.circle, extraStyle]} />;\n  }\n});\n\nconst CIRCLE_SIZE = 4;\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  innerContainer: {\n    flexDirection: \"row\"\n  },\n  circle: {\n    margin: 2,\n    width: CIRCLE_SIZE,\n    height: CIRCLE_SIZE,\n    borderRadius: CIRCLE_SIZE / 2\n  },\n  full: {\n    backgroundColor: \"#fff\"\n  },\n  empty: {\n    backgroundColor: \"#fff5\"\n  }\n});\n\nmodule.exports = F8PageControl;\nmodule.exports.__cards__ = define => {\n  define(\"Simple 2\", () => <F8PageControl count={2} selectedIndex={0} />);\n  define(\"Simple 5\", () => <F8PageControl count={5} selectedIndex={2} />);\n};\n"
  },
  {
    "path": "js/common/F8ScrollingHeader.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { PixelRatio, StyleSheet, Animated } from \"react-native\";\n\n/* constants ================================================================ */\n\nconst TRANSLATE_Y_DISTANCE = 10;\n\n/**\n* ==============================================================================\n* <F8ScrollingHeader />\n* ------------------------------------------------------------------------------\n* @param {string} text the title text\n* @param {?number} trigger the scroll position to trigger intro/outro animation\n* @param {?number} duration intro/outro animation duration\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nexport default class F8ScrollingHeader extends React.Component {\n  static defaultProps = {\n    trigger: 200,\n    duration: 180,\n    contentInset: 30\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      revealed: false,\n      anim: new Animated.Value(0)\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (nextProps.scrollTop !== this.props.scrollTop) {\n      let toValue = null;\n      const { revealed } = this.state;\n      const { trigger, scrollTop, duration } = nextProps;\n      if (!revealed && scrollTop >= trigger) {\n        toValue = 1;\n      } else if (revealed && scrollTop < trigger) {\n        toValue = 0;\n      }\n      if (toValue !== null) {\n        this.setState({ revealed: toValue === 1 ? true : false });\n        Animated.timing(this.state.anim, { toValue, duration }).start();\n      }\n    }\n  }\n\n  render() {\n    const { text } = this.props;\n\n    return (\n      <Animated.View\n        style={[\n          styles.container,\n          {\n            left: Math.max(this.props.contentInset - 6, 0),\n            right: Math.max(this.props.contentInset - 6, 0)\n          },\n          { opacity: this.state.anim, overflow: \"hidden\" }\n        ]}\n      >\n        <Animated.Text\n          numberOfLines={1}\n          style={[\n            styles.text,\n            {\n              transform: [\n                {\n                  translateY: this.state.anim.interpolate({\n                    inputRange: [0, 1],\n                    outputRange: [-TRANSLATE_Y_DISTANCE, 0]\n                  })\n                }\n              ]\n            }\n          ]}\n        >\n          {text}\n        </Animated.Text>\n      </Animated.View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    backgroundColor: \"white\",\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    position: \"absolute\",\n    top: 0,\n    paddingVertical: 9,\n    paddingHorizontal: 6,\n    borderBottomWidth: 1 / PixelRatio.get(),\n    borderBottomColor: \"rgba(153, 162, 178, 1)\"\n  },\n  text: {\n    fontSize: 13,\n    color: F8Colors.tangaroa\n  }\n});\n"
  },
  {
    "path": "js/common/F8SegmentedControl.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport { Text } from \"./F8Text\";\nimport F8Colors from \"./F8Colors\";\nimport F8Fonts from \"./F8Fonts\";\nimport { View, TouchableOpacity } from \"react-native\";\n\n/* constants ================================================================ */\n\nconst BUTTON_HEIGHT = 32,\n  CONTAINER_PADDING_B = 12,\n  NOTIFICATION_ICON_SIZE = 5;\n\n/* <F8SegmentedControl />\n============================================================================= */\n\nclass F8SegmentedControl extends React.Component {\n  props: {\n    values: Array<string>,\n    selectedIndex: number,\n    onChange: (newIndex: number) => void,\n    style?: any\n  };\n\n  static defaultProps = {\n    backgroundColor: F8Colors.blue,\n    textColor: F8Colors.white,\n    borderColor: F8Colors.white\n  };\n\n  render() {\n    const { backgroundColor, borderColor, textColor } = this.props;\n\n    const segments = this.props.values.map((value, index) => (\n      <Segment\n        key={value.title}\n        value={value.title}\n        textColor={textColor}\n        borderColor={borderColor}\n        hasUpdates={value.hasUpdates}\n        isSelected={index === this.props.selectedIndex}\n        onPress={() => this.props.onChange(index)}\n      />\n    ));\n    return (\n      <View style={[styles.container, { backgroundColor }, this.props.style]}>\n        {segments}\n      </View>\n    );\n  }\n}\n\nclass Segment extends React.Component {\n  props: {\n    value: string,\n    borderColor: ?string,\n    textColor: ?string,\n    isSelected: boolean,\n    onPress: () => void\n  };\n\n  render() {\n    const { value, isSelected, borderColor, textColor } = this.props;\n\n    const title = value && value.toUpperCase();\n\n    let selectedButtonStyle;\n    if (isSelected) {\n      selectedButtonStyle = { borderColor };\n    }\n\n    let accessibilityTraits = [\"button\"];\n    if (isSelected) {\n      accessibilityTraits.push(\"selected\");\n    }\n\n    return (\n      <TouchableOpacity\n        accessibilityTraits={accessibilityTraits}\n        activeOpacity={0.8}\n        onPress={this.props.onPress}\n        style={[styles.button, selectedButtonStyle]}\n      >\n        <Text style={[styles.label, { color: textColor }]}>{title}</Text>\n        {this.renderNotificationIcon()}\n      </TouchableOpacity>\n    );\n  }\n\n  renderNotificationIcon() {\n    if (this.props.hasUpdates) {\n      return <View style={styles.notificationIcon} />;\n    } else {\n      return null;\n    }\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    flexDirection: \"row\",\n    paddingBottom: CONTAINER_PADDING_B,\n    justifyContent: \"center\",\n    alignItems: \"center\"\n  },\n  button: {\n    borderColor: \"transparent\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    backgroundColor: \"transparent\",\n    height: BUTTON_HEIGHT,\n    paddingHorizontal: 20,\n    borderRadius: BUTTON_HEIGHT / 2,\n    borderWidth: 1\n  },\n  label: {\n    fontSize: 13,\n    color: \"white\",\n    fontFamily: F8Fonts.helvetica\n  },\n  notificationIcon: {\n    position: \"absolute\",\n    right: 11,\n    top: 7,\n    width: NOTIFICATION_ICON_SIZE,\n    height: NOTIFICATION_ICON_SIZE,\n    backgroundColor: F8Colors.yellow,\n    borderRadius: NOTIFICATION_ICON_SIZE / 2\n  }\n});\n\n/* exports ================================================================== */\nmodule.exports = F8SegmentedControl;\n"
  },
  {
    "path": "js/common/F8StyleSheet.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { StyleSheet, Platform } from \"react-native\";\n\nexport default {\n  create(styles: Object): { [name: string]: number } {\n    const platformStyles = {};\n    Object.keys(styles).forEach(name => {\n      let { ios, android, ...style } = { ...styles[name] };\n      if (ios && Platform.OS === \"ios\") {\n        style = { ...style, ...ios };\n      }\n      if (android && Platform.OS === \"android\") {\n        style = { ...style, ...android };\n      }\n      platformStyles[name] = style;\n    });\n    return StyleSheet.create(platformStyles);\n  }\n};\n"
  },
  {
    "path": "js/common/F8Text.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\n\"use strict\";\n\n// Dependencies\n// =============================================================================\n\nimport React from \"react\";\nimport ReactNative from \"react-native\";\nimport F8Colors from \"./F8Colors\";\nimport F8Fonts from \"./F8Fonts\";\nimport StyleSheet from \"./F8StyleSheet\";\n\n// Text Elements\n// =============================================================================\n\nexport function Text({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.text, style]} {...props} />;\n}\n\nexport function Heading1({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.h1, style]} {...props} />;\n}\n\nexport function Heading2({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.h2, style]} {...props} />;\n}\n\nexport function Heading3({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.h3, style]} {...props} />;\n}\n\nexport function Heading4({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.h4, style]} {...props} />;\n}\n\nexport function Heading5({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.h5, style]} {...props} />;\n}\n\nexport function Paragraph({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.p, style]} {...props} />;\n}\n\n// export function Hyperlink({style, ...props}: Object): ReactElement<ReactNative.Text> {\n//   return <ReactNative.Text style={[styles.a, style]} {...props} />;\n// }\n\nexport function HeaderTitle({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.Text style={[styles.headerTitle, style]} {...props} />;\n}\n\nexport function HorizontalRule({\n  style,\n  ...props\n}: Object): ReactElement<ReactNative.Text> {\n  return <ReactNative.View style={[styles.hr, style]} {...props} />;\n}\n\n// Styles\n// =============================================================================\n\nconst styles = StyleSheet.create({\n  text: {\n    fontFamily: F8Fonts.default\n  },\n  h1: {\n    fontFamily: F8Fonts.h1,\n    fontSize: F8Fonts.normalize(30),\n    lineHeight: F8Fonts.lineHeight(37),\n    color: F8Colors.blue\n  },\n  h2: {\n    fontFamily: F8Fonts.h2,\n    fontSize: F8Fonts.normalize(23),\n    lineHeight: F8Fonts.lineHeight(27), //, 1.4\n    color: F8Colors.tangaroa,\n    letterSpacing: -0.24\n  },\n  h3: {\n    fontFamily: F8Fonts.h3,\n    fontSize: F8Fonts.normalize(17),\n    lineHeight: F8Fonts.lineHeight(20),\n    color: F8Colors.sapphire,\n    letterSpacing: -0.11\n  },\n  h4: {\n    fontFamily: F8Fonts.h4,\n    fontSize: F8Fonts.normalize(13),\n    lineHeight: F8Fonts.lineHeight(22),\n    color: F8Colors.tangaroa\n  },\n  h5: {\n    fontFamily: F8Fonts.helvetica,\n    fontSize: F8Fonts.normalize(13),\n    lineHeight: F8Fonts.lineHeight(22),\n    color: F8Colors.tangaroa\n  },\n  p: {\n    fontFamily: F8Fonts.p,\n    fontSize: F8Fonts.normalize(17),\n    lineHeight: F8Fonts.lineHeight(25), //, 1.25\n    color: F8Colors.tangaroa\n  },\n  // a: {\n  //   color: F8Colors.blue,\n  //   textDecorationLine: 'underline',\n  // },\n  hr: {\n    height: 1,\n    backgroundColor: F8Colors.colorWithAlpha(\"black\", 0.1)\n  },\n  headerTitle: {\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\"),\n    ios: { fontSize: 17 },\n    android: { fontSize: 20 }\n  }\n});\n"
  },
  {
    "path": "js/common/F8TimelineBackground.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\nimport React from \"react\";\nimport { View } from \"react-native\";\nimport LinearGradient from \"react-native-linear-gradient\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport F8Colors from \"./F8Colors\";\nimport F8TimelineSegment from \"./F8TimelineSegment\";\n\n/* =============================================================================\n<F8TimelineBackground />\n============================================================================= */\n\nclass F8TimelineBackground extends React.Component {\n  static defaultProps = {\n    left: 77,\n    height: 118,\n    fadeOut: true\n  };\n\n  render() {\n    const { style, left, fadeOut, height } = this.props;\n    let gradient = fadeOut ? this.renderGradient() : null;\n    return (\n      <View style={[styles.container, { height }, style]}>\n        <F8TimelineSegment left={left} dot={false} />\n        {gradient}\n      </View>\n    );\n  }\n\n  renderGradient() {\n    return (\n      <LinearGradient\n        pointerEvents=\"none\"\n        start={{ x: 0.5, y: 0 }}\n        end={{ x: 0.5, y: 1 }}\n        style={styles.gradient}\n        colors={[\n          F8Colors.colorWithAlpha(\"bianca\", 0),\n          F8Colors.colorWithAlpha(\"bianca\", 1)\n        ]}\n      />\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\nconst styles = StyleSheet.create({\n  container: {},\n  gradient: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0\n  }\n});\n\n/* Export\n============================================================================= */\nexport default F8TimelineBackground;\n"
  },
  {
    "path": "js/common/F8TimelineSegment.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\nimport React from \"react\";\nimport { View, Image } from \"react-native\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport F8Colors from \"./F8Colors\";\n\n/* =============================================================================\n<F8TimelineSegment />\n--------------------------------------------------------------------------------\nDescription:\n  Timeline & dot component to be used in table cells\nProps:\n  ? left:number           -> absolute offset from left of parent view\n  ? color:string          -> timeline color\n  ? dotColor:string       -> default dot color\n  ? dotColorActive:string -> active dot color\n  ? dotOffsetTop:number   -> offset from (to align with text)\n\n============================================================================= */\n\nclass F8TimelineSegment extends React.Component {\n  static defaultProps = {\n    line: true,\n    dot: true,\n    left: 24,\n    lineOffsetTop: 0,\n    dotOffsetTop: 0,\n    dotSize: 7,\n    lineWidth: 1,\n    color: F8Colors.colorWithAlpha(\"tangaroa\", 0.2), // TODO: Should be 0.4 alpha\n    dotColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.2),\n    active: false,\n    dotColorActive: F8Colors.pink\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.styles = StyleSheet.create({\n      container: {\n        position: \"absolute\",\n        left: props.left - props.dotSize / 2,\n        width: props.dotSize,\n        top: 0,\n        bottom: 0\n      },\n      timelineLine: {\n        position: \"absolute\",\n        left: props.dotSize / 2 - props.lineWidth / 2,\n        top: props.lineOffsetTop,\n        bottom: 0,\n        width: props.lineWidth,\n        backgroundColor: props.color\n      },\n      timelineDot: {\n        position: \"absolute\",\n        width: props.dotSize,\n        height: props.dotSize,\n        borderRadius: props.dotSize / 2,\n        top: props.dotOffsetTop,\n        left: 0,\n        borderWidth: 1,\n        backgroundColor: F8Colors.bianca,\n        borderColor: props.dotColor\n      },\n      timelineDotActive: {\n        backgroundColor: props.dotColorActive\n      }\n    });\n  }\n\n  render() {\n    return (\n      <View pointerEvents=\"none\" style={this.styles.container}>\n        {this.renderLine()}\n        {this.renderDot()}\n      </View>\n    );\n  }\n\n  renderLine() {\n    if (!this.props.line) {\n      return null;\n    } else {\n      return <View style={this.styles.timelineLine} />;\n    }\n  }\n\n  renderDot() {\n    if (!this.props.dot) {\n      return null;\n    }\n    const {\n      active,\n      dotIconDefault,\n      dotIconActive,\n      dotOffsetTop,\n      dotSize\n    } = this.props;\n    if (dotIconDefault && dotIconActive) {\n      return (\n        <Image\n          source={active ? dotIconActive : dotIconDefault}\n          style={{\n            position: \"absolute\",\n            width: dotSize,\n            height: dotSize,\n            top: dotOffsetTop,\n            left: 0\n          }}\n        />\n      );\n    } else {\n      return (\n        <View\n          style={[\n            this.styles.timelineDot,\n            active ? this.styles.timelineDotActive : null\n          ]}\n        />\n      );\n    }\n  }\n}\n\n/* Export\n============================================================================= */\nexport default F8TimelineSegment;\n"
  },
  {
    "path": "js/common/F8Toast.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { Heading3, Paragraph } from \"./F8Text\";\nimport { Modal, Animated, View, StyleSheet } from \"react-native\";\n\n/* constants ================================================================ */\n\nconst INTRO_DELAY_DUR = 300,\n  SHOW_DELAY_DUR = 800,\n  OUTRO_ANIM_DUR = 150,\n  TRANSLATE_Y_DISTANCE = 60;\n\n/**\n* ==============================================================================\n* <F8Toast />\n* ------------------------------------------------------------------------------\n* @param {?function} onComplete Outro animation callback\n* @param {?string} title At least one of title/text is required\n* @param {?string} text At least one of title/text is required\n* @param {?string} backgroundColor Container background color\n* @param {?string} titleColor Heading text color\n* @param {?string} textColor Paragraph text color\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8Toast extends React.Component {\n  static defaultProps = {\n    backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.95),\n    titleColor: F8Colors.white,\n    textColor: F8Colors.white,\n    onComplete: _ => {}\n  };\n\n  constructor() {\n    super();\n\n    this.state = {\n      contentAnimation: new Animated.Value(0)\n    };\n\n    this.animatedContentStyles = {\n      opacity: this.state.contentAnimation,\n      transform: [\n        {\n          translateY: this.state.contentAnimation.interpolate({\n            inputRange: [0, 1],\n            outputRange: [TRANSLATE_Y_DISTANCE, 0]\n          })\n        }\n      ]\n    };\n\n    this.intro();\n  }\n\n  render() {\n    const { title, text, backgroundColor, titleColor, textColor } = this.props;\n    if (!title && !text) {\n      return null;\n    }\n\n    return (\n      <Modal transparent={true} animationType=\"fade\" visible={true}>\n        <View style={styles.container}>\n          <Animated.View\n            style={[\n              styles.content,\n              { backgroundColor },\n              this.animatedContentStyles\n            ]}\n          >\n            {title ? (\n              <Heading3 style={[styles.title, { color: titleColor }]}>\n                {title}\n              </Heading3>\n            ) : null}\n            {text ? (\n              <Paragraph style={[styles.text, { color: textColor }]}>\n                {text}\n              </Paragraph>\n            ) : null}\n          </Animated.View>\n        </View>\n      </Modal>\n    );\n  }\n\n  intro = _ => {\n    Animated.spring(this.state.contentAnimation, {\n      delay: INTRO_DELAY_DUR,\n      toValue: 1\n    }).start(this.outro);\n  };\n\n  outro = _ => {\n    Animated.timing(this.state.contentAnimation, {\n      delay: SHOW_DELAY_DUR,\n      toValue: 0,\n      duration: OUTRO_ANIM_DUR\n    }).start(this.props.onComplete);\n  };\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    padding: 40,\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  content: {\n    paddingVertical: 14,\n    paddingHorizontal: 36,\n    borderRadius: 4\n  },\n  title: {\n    marginBottom: 4,\n    textAlign: \"center\"\n  },\n  text: {\n    textAlign: \"center\"\n  }\n});\n\n/* exports & Playground cards =============================================== */\n\nmodule.exports = F8Toast;\nmodule.exports.__cards__ = define => {\n  define(\"Default\", _ => <F8Toast text=\"Thanks for your review!\" />);\n};\n"
  },
  {
    "path": "js/common/F8Tooltip.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { HeaderTitle, Text } from \"./F8Text\";\nimport { Modal, Animated, View, StyleSheet, Image } from \"react-native\";\nimport Hitbox from \"./Hitbox\";\n\nconst ARROW_WIDTH = 23,\n  ARROW_HEIGHT = 13,\n  TOOLTIP_INSET = 23,\n  TRANSLATE_Y_DISTANCE = 10;\n\n/**\n* ==============================================================================\n* <F8Tooltip />\n* ------------------------------------------------------------------------------\n* @param {?string} title at least one of title/text is required (no content, no render)\n* @param {?string} text at least one of title/text is required (no content, no render)\n* @param {?number} x x position\n* @param {?number} y x position\n* @param {?string} align top or bottom placement of tooltip // TODO\n* @param {?string} hitboxColor background overlay color\n* @param {?function} onDismiss Callback\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8Tooltip extends React.Component {\n  static defaultProps = {\n    x: 0,\n    y: 0,\n    align: \"top\",\n    hitboxColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.2),\n    onDismiss: _ => {}\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      anim: new Animated.Value(props.visible ? 1 : 0)\n    };\n\n    this.animatedTransforms = {\n      transform: [\n        {\n          translateY: this.state.anim.interpolate({\n            inputRange: [0, 1],\n            outputRange: [TRANSLATE_Y_DISTANCE, 0]\n          })\n        }\n      ]\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (nextProps.visible !== this.props.visible && nextProps.visible) {\n      this.state.anim.setValue(0);\n      Animated.spring(this.state.anim, { toValue: 1 }).start();\n    }\n  }\n\n  render() {\n    const { x, y, visible, title, text, hitboxColor, onDismiss } = this.props;\n    if (!title && !text) {\n      return null;\n    }\n\n    const { left, top } = this.getArrowPosition(x, y);\n\n    return (\n      <Modal\n        style={styles.modal}\n        visible={visible}\n        transparent={true}\n        animationType=\"fade\"\n      >\n        <Hitbox onPress={onDismiss} style={{ backgroundColor: hitboxColor }} />\n        <Animated.View\n          pointerEvents=\"box-none\"\n          style={[styles.tooltip, this.animatedTransforms, { top }]}\n        >\n          <Image\n            style={[styles.arrow, { left }]}\n            source={require(\"./img/tooltip-arrow.png\")}\n          />\n          <View style={styles.content}>\n            {title ? (\n              <HeaderTitle style={styles.title}>{title}</HeaderTitle>\n            ) : null}\n            {text ? <Text style={styles.text}>{text}</Text> : null}\n          </View>\n        </Animated.View>\n      </Modal>\n    );\n  }\n\n  getArrowPosition(x, y) {\n    const left = x - ARROW_WIDTH / 2;\n    const top = y;\n    return { left, top };\n  }\n}\n\n// StyleSheet ==================================================================\n\nconst styles = StyleSheet.create({\n  modal: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0\n  },\n\n  hitbox: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0\n  },\n\n  tooltip: {\n    position: \"absolute\",\n    top: 0,\n    left: 0,\n    right: 0,\n    padding: TOOLTIP_INSET\n  },\n\n  arrow: {\n    position: \"absolute\",\n    left: 0,\n    top: TOOLTIP_INSET - ARROW_HEIGHT + 1 // prevent hairline separation\n  },\n\n  content: {\n    backgroundColor: F8Colors.white,\n    borderRadius: 4,\n    paddingTop: 21,\n    paddingBottom: 16, // 5 less to account for marginBottom's\n    paddingHorizontal: 24,\n    alignItems: \"center\"\n  },\n  title: {\n    marginBottom: 5,\n    color: F8Colors.blue,\n    textAlign: \"center\"\n  },\n  text: {\n    marginBottom: 5,\n    textAlign: \"center\"\n  }\n});\n\n// export ======================================================================\n\nexport default F8Tooltip;\n"
  },
  {
    "path": "js/common/F8Touchable.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport {\n  TouchableHighlight,\n  TouchableNativeFeedback,\n  Platform\n} from \"react-native\";\n\nfunction F8TouchableIOS(props: Object): ReactElement<TouchableHighlight> {\n  return (\n    <TouchableHighlight\n      accessibilityTraits=\"button\"\n      underlayColor=\"rgba(0,0,0,0.05)\"\n      {...props}\n    />\n  );\n}\n\nconst F8Touchable =\n  Platform.OS === \"android\" ? TouchableNativeFeedback : F8TouchableIOS;\n\nmodule.exports = F8Touchable;\n"
  },
  {
    "path": "js/common/F8WebView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport F8Header from \"./F8Header\";\nimport F8Linking from \"./F8Linking\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport {\n  Platform,\n  InteractionManager,\n  View,\n  Keyboard,\n  PixelRatio,\n  WebView,\n  TouchableOpacity,\n  Image\n} from \"react-native\";\n\n/* constants ================================================================ */\n\nconst NAVBAR_HEIGHT_IOS = 45,\n  STATUS_BAR_HEIGHT_ANDROID =\n    Platform.OS === \"android\" && Platform.Version && Platform.Version < 21\n      ? 0\n      : 25,\n  NAVBAR_HEIGHT_ANDROID = 55 + STATUS_BAR_HEIGHT_ANDROID, // old android status bar issue (fix from F8Header)\n  DISABLED_OPACITY = 0.3;\n\n/**\n* ==============================================================================\n* <F8WebView />\n* ------------------------------------------------------------------------------\n* @param {string} url WebView source prop\n* @param {AnyObject} navigator F8Navigator for back button 'pop'\n* @param {?string} backgroundColor Optional header background color\n* @param {?string} titleColor Optional header title color\n* @param {?string} itemsColor Optional header items color\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8WebView extends React.Component {\n  static defaultProps = {\n    backgroundColor: F8Colors.salmon,\n    titleColor: F8Colors.white,\n    itemsColor: F8Colors.white\n  };\n\n  constructor(props) {\n    super(props);\n\n    const { url } = this.props;\n    this.state = {\n      url,\n      canNavigateBack: false,\n      canNavigateForward: false\n    };\n  }\n\n  render() {\n    return (\n      <View style={styles.container}>\n        {this.renderPlatformHeader()}\n        <Loading>\n          <View style={{ flex: 1 }}>\n            <WebView\n              ref={c => (this._webview = c)}\n              onNavigationStateChange={this.onNavigationStateChange}\n              style={styles.webview}\n              source={{ uri: this.props.url }}\n              scalesPageToFit={true}\n            />\n            {Platform.OS === \"ios\" ? this.renderNavigationBarIOS() : null}\n          </View>\n        </Loading>\n      </View>\n    );\n  }\n\n  renderPlatformHeader() {\n    const { backgroundColor, titleColor, itemsColor } = this.props;\n\n    if (Platform.OS === \"ios\") {\n      return (\n        <F8Header\n          backgroundColor={backgroundColor}\n          titleColor={titleColor}\n          itemsColor={itemsColor}\n          leftItem={{\n            title: \"Done\",\n            onPress: this.dismiss\n          }}\n        >\n          <Image\n            style={{ tintColor: titleColor }}\n            source={require(\"./img/webview/logo.png\")}\n          />\n        </F8Header>\n      );\n    } else {\n      return (\n        <WebViewNavigationAndroid\n          backgroundColor={backgroundColor}\n          titleColor={titleColor}\n          itemsColor={itemsColor}\n          canBack={this.state.canNavigateBack}\n          canForward={this.state.canNavigateForward}\n          onDismiss={this.dismiss}\n          onBack={_ => this.navigate(\"BACK\")}\n          onForward={_ => this.navigate(\"FORWARD\")}\n          onBrowser={_ => this.navigate(\"BROWSER\")}\n        />\n      );\n    }\n  }\n\n  renderNavigationBarIOS() {\n    return (\n      <WebViewNavigationIOS\n        canBack={this.state.canNavigateBack}\n        canForward={this.state.canNavigateForward}\n        onBack={_ => this.navigate(\"BACK\")}\n        onForward={_ => this.navigate(\"FORWARD\")}\n        onBrowser={_ => this.navigate(\"BROWSER\")}\n      />\n    );\n  }\n\n  navigate(intent) {\n    if (intent === \"BACK\" && this.state.canNavigateBack) {\n      this._webview.goBack && this._webview.goBack();\n    } else if (intent === \"FORWARD\" && this.state.canNavigateForward) {\n      this._webview.goForward && this._webview.goForward();\n    } else if (intent === \"BROWSER\") {\n      F8Linking.openURL(this.state.url);\n    }\n  }\n\n  onNavigationStateChange = navState => {\n    this.setState({\n      canNavigateBack: navState.canGoBack,\n      canNavigateForward: navState.canGoForward,\n      url: navState.url\n    });\n  };\n\n  dismiss = _ => {\n    this.props.navigator.pop();\n    Keyboard.dismiss();\n  };\n}\n\n/* =============================================================================\n<Loading /> (iOS & Android)\n--------------------------------------------------------------------------------\nWait until WebView is ready before rendering children.\n============================================================================= */\nclass Loading extends React.Component {\n  state = {\n    loaded: false\n  };\n\n  componentDidMount() {\n    InteractionManager.runAfterInteractions(() =>\n      this.setState({ loaded: true })\n    );\n  }\n\n  render() {\n    if (this.state.loaded) {\n      return React.Children.only(this.props.children);\n    }\n    return null;\n  }\n}\n\n/* =============================================================================\n<WebViewNavigationIOS />\n--------------------------------------------------------------------------------\nBack, forward and open in a browser\n============================================================================= */\nclass WebViewNavigationIOS extends React.Component {\n  render() {\n    return (\n      <View style={styles.navBar}>\n        <View style={styles.navBarArrows}>\n          <TouchableOpacity\n            onPress={_ => this.props.onBack && this.props.onBack()}\n            disabled={!this.props.canBack}\n            style={styles.navBarAction}\n          >\n            <Image\n              style={!this.props.canBack ? { opacity: DISABLED_OPACITY } : null}\n              source={require(\"./img/webview/back.png\")}\n            />\n          </TouchableOpacity>\n\n          <TouchableOpacity\n            onPress={_ => this.props.onForward && this.props.onForward()}\n            disabled={!this.props.canForward}\n            style={styles.navBarAction}\n          >\n            <Image\n              style={\n                !this.props.canForward ? { opacity: DISABLED_OPACITY } : null\n              }\n              source={require(\"./img/webview/forward.png\")}\n            />\n          </TouchableOpacity>\n        </View>\n\n        <TouchableOpacity\n          onPress={_ => this.props.onBrowser && this.props.onBrowser()}\n          style={styles.navBarAction}\n        >\n          <Image source={require(\"./img/webview/browser.png\")} />\n        </TouchableOpacity>\n      </View>\n    );\n  }\n}\n\n/* =============================================================================\n<WebViewNavigationAndroid />\n--------------------------------------------------------------------------------\nBack, forward and open in a browser\n============================================================================= */\nclass WebViewNavigationAndroid extends React.Component {\n  render() {\n    const { backgroundColor, itemsColor } = this.props;\n\n    return (\n      <View style={[styles.navBar, { backgroundColor }]}>\n        <View style={styles.navBarContent}>\n          <TouchableOpacity\n            onPress={_ => this.props.onDismiss && this.props.onDismiss()}\n            style={styles.navBarDismiss}\n          >\n            <Image\n              style={{ tintColor: itemsColor }}\n              source={require(\"./img/webview/close.png\")}\n            />\n          </TouchableOpacity>\n\n          <View style={styles.navBarArrows}>\n            <TouchableOpacity\n              onPress={_ => this.props.onBack && this.props.onBack()}\n              disabled={!this.props.canBack}\n              style={styles.navBarAction}\n            >\n              <Image\n                style={[\n                  { tintColor: itemsColor },\n                  !this.props.canBack ? { opacity: DISABLED_OPACITY } : null\n                ]}\n                source={require(\"./img/webview/back.png\")}\n              />\n            </TouchableOpacity>\n\n            <TouchableOpacity\n              onPress={_ => this.props.onForward && this.props.onForward()}\n              disabled={!this.props.canForward}\n              style={styles.navBarAction}\n            >\n              <Image\n                style={[\n                  { tintColor: itemsColor },\n                  !this.props.canForward ? { opacity: DISABLED_OPACITY } : null\n                ]}\n                source={require(\"./img/webview/forward.png\")}\n              />\n            </TouchableOpacity>\n\n            <TouchableOpacity\n              onPress={_ => this.props.onBrowser && this.props.onBrowser()}\n              style={styles.navBarAction}\n            >\n              <Image\n                style={{ tintColor: itemsColor }}\n                source={require(\"./img/webview/browser.png\")}\n              />\n            </TouchableOpacity>\n          </View>\n        </View>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.bianca\n  },\n  webview: {\n    flex: 1\n  },\n\n  navBar: {\n    ios: {\n      flexDirection: \"row\",\n      justifyContent: \"space-between\",\n      height: NAVBAR_HEIGHT_IOS,\n      backgroundColor: F8Colors.bianca,\n      borderTopWidth: 1 / PixelRatio.get(),\n      borderColor: F8Colors.magnesium\n    },\n    android: {\n      height: NAVBAR_HEIGHT_ANDROID,\n      paddingHorizontal: 10,\n      paddingBottom: 10,\n      justifyContent: \"flex-end\"\n    }\n  },\n\n  navBarContent: {\n    android: {\n      flexDirection: \"row\",\n      justifyContent: \"space-between\",\n      alignItems: \"center\"\n    }\n  },\n\n  navBarArrows: {\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n\n    ios: {\n      width: 150\n    },\n    android: {\n      width: 122,\n      alignItems: \"center\"\n    }\n  },\n\n  navBarDismiss: {\n    android: {\n      padding: 5\n    }\n  },\n\n  navBarAction: {\n    ios: {\n      justifyContent: \"center\",\n      paddingHorizontal: 15\n    },\n    android: {\n      padding: 5\n    }\n  }\n});\n\n/* playground cards ========================================================= */\n\nconst webView = F8WebView;\nwebView.__cards__ = define => {\n  define(\"Default\", _ => <F8WebView url=\"https://fbf8.com\" />);\n};\n\n/* exports ================================================================== */\nmodule.exports = webView;\n"
  },
  {
    "path": "js/common/Hitbox.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { TouchableWithoutFeedback, View } from \"react-native\";\n\n/**\n* ==============================================================================\n* <Hitbox />\n* ------------------------------------------------------------------------------\n* @param {function} onPress Callback\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nexport default class Hitbox extends React.Component {\n  render() {\n    return (\n      <TouchableWithoutFeedback onPress={this.props.onPress}>\n        <View\n          style={[\n            { position: \"absolute\", top: 0, left: 0, right: 0, bottom: 0 },\n            this.props.style\n          ]}\n        />\n      </TouchableWithoutFeedback>\n    );\n  }\n}\n"
  },
  {
    "path": "js/common/ItemsWithSeparator.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { PixelRatio, StyleSheet, View } from \"react-native\";\n\nclass ItemsWithSeparator extends React.Component {\n  props: {\n    style?: any,\n    separatorStyle?: any,\n    children?: any\n  };\n\n  render() {\n    const children = [];\n    const length = React.Children.count(this.props.children);\n    React.Children.forEach(this.props.children, (child, ii) => {\n      children.push(child);\n      if (ii !== length - 1) {\n        children.push(\n          <View\n            key={\"separator-\" + ii}\n            style={[styles.separator, this.props.separatorStyle]}\n          />\n        );\n      }\n    });\n    return <View style={this.props.style}>{children}</View>;\n  }\n}\n\nconst styles = StyleSheet.create({\n  separator: {\n    backgroundColor: \"#0322500A\",\n    height: 1 / PixelRatio.get()\n  }\n});\n\nmodule.exports = ItemsWithSeparator;\n"
  },
  {
    "path": "js/common/LaunchScreen.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { Dimensions, StyleSheet, View, Image } from \"react-native\";\n\nconst WIN_WIDTH = Dimensions.get(\"window\").width,\n  WIN_HEIGHT = Dimensions.get(\"window\").height;\n\n/**\n* ==============================================================================\n* <LaunchScreen />\n* ------------------------------------------------------------------------------\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass LaunchScreen extends React.Component {\n  static __cards__;\n\n  render() {\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <Image\n          source={require(\"./img/launchscreen.png\")}\n          style={styles.image}\n        />\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.bianca\n  },\n  image: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    width: WIN_WIDTH,\n    height: WIN_HEIGHT,\n    resizeMode: \"cover\"\n  }\n});\n\n/* playground cards ========================================================= */\n\nconst launchScreen = LaunchScreen;\nlaunchScreen.__cards__ = define => {\n  define(\"Default\", _ => <LaunchScreen />);\n};\n\n/* exports ================================================================== */\nmodule.exports = launchScreen;\n"
  },
  {
    "path": "js/common/ListContainer.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport Animated from \"Animated\";\nimport NativeModules from \"NativeModules\";\nimport F8Header from \"./F8Header\";\nimport F8SegmentedControl from \"./F8SegmentedControl\";\nimport React from \"react\";\nimport ReactNative from \"react-native\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport View from \"View\";\nimport ViewPager from \"./ViewPager\";\n\nimport F8Colors from \"./F8Colors\";\nimport ActionsOverlay from \"./ActionsOverlay\";\n\nimport type { Item as HeaderItem } from \"./F8Header\";\n\ntype Props = {\n  title: string,\n  leftItem?: HeaderItem,\n  rightItem?: HeaderItem,\n  extraItems?: Array<HeaderItem>,\n  selectedSegment?: number,\n  selectedSectionColor: string,\n  backgroundImage: number,\n  backgroundColor: string,\n  parallaxContent?: ?ReactElement,\n  stickyHeader?: ?ReactElement,\n  onSegmentChange?: (segment: number) => void,\n  children?: any\n};\n\ntype State = {\n  idx: number,\n  anim: Animated.Value,\n  stickyHeaderHeight: number\n};\n\nclass ListContainer extends React.Component {\n  props: Props;\n  state: State;\n  _refs: Array<any>;\n  _pinned: any;\n\n  static defaultProps = {\n    selectedSectionColor: \"white\"\n  };\n\n  static contextTypes = {\n    openDrawer: React.PropTypes.func,\n    hasUnreadNotifications: React.PropTypes.number\n  };\n\n  constructor(props: Props) {\n    super(props);\n\n    this.state = {\n      idx: this.props.selectedSegment || 0,\n      stickyHeaderHeight: 0\n    };\n\n    (this: any).handleSelectSegment = this.handleSelectSegment.bind(this);\n    this._refs = [];\n  }\n\n  render() {\n    const segments = [];\n    const content = React.Children.map(this.props.children, (child, idx) => {\n      segments.push({\n        title: child.props.title,\n        hasUpdates: child.props.hasUpdates\n      });\n      return React.cloneElement(child, {\n        ref: ref => {\n          this._refs[idx] = ref;\n        },\n        // onScroll: (e) => this.handleScroll(idx, e),\n        style: styles.listView,\n        showsVerticalScrollIndicator: false,\n        scrollEventThrottle: 16,\n        // contentInset: {bottom: 141, top: 0},\n        automaticallyAdjustContentInsets: false,\n        // renderHeader: this.renderFakeHeader,\n        scrollsToTop: idx === this.state.idx\n      });\n    });\n\n    let { stickyHeader } = this.props;\n    if (segments.length > 1) {\n      stickyHeader = (\n        <View>\n          <F8SegmentedControl\n            values={segments}\n            selectedIndex={this.state.idx}\n            onChange={this.handleSelectSegment}\n            backgroundColor={this.props.headerBackgroundColor}\n            textColor={this.props.segmentedTextColor}\n            borderColor={this.props.segmentedBorderColor}\n          />\n          {stickyHeader}\n        </View>\n      );\n    }\n\n    let modalClose;\n    if (this.props.modalClose) {\n      modalClose = <ActionsOverlay onPress={this.props.modalClose} />;\n    }\n\n    return (\n      <View style={styles.container}>\n        <View style={styles.headerWrapper}>\n          <F8Header\n            title={this.props.title}\n            type={this.props.headerType}\n            navItem={this.props.navItem}\n            leftItem={this.props.leftItem}\n            rightItem={this.props.rightItem}\n            extraItems={this.props.extraItems}\n            backgroundColor={this.props.headerBackgroundColor}\n            titleColor={this.props.headerTitleColor}\n            itemsColor={this.props.headerItemsColor}\n          >\n            {this.props.headerContent}\n          </F8Header>\n          {stickyHeader}\n        </View>\n        <ViewPager\n          count={segments.length}\n          selectedIndex={this.state.idx}\n          onSelectedIndexChange={this.handleSelectSegment}\n        >\n          {content}\n        </ViewPager>\n        {modalClose}\n      </View>\n    );\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    if (\n      typeof nextProps.selectedSegment === \"number\" &&\n      nextProps.selectedSegment !== this.state.idx\n    ) {\n      this.setState({ idx: nextProps.selectedSegment });\n    }\n  }\n\n  componentDidUpdate(prevProps: Props, prevState: State) {\n    if (!NativeModules.F8Scrolling) {\n      return;\n    }\n\n    if (\n      this.state.idx !== prevState.idx ||\n      this.state.stickyHeaderHeight !== prevState.stickyHeaderHeight\n    ) {\n      if (\n        this._refs[prevState.idx] &&\n        this._refs[prevState.idx].getScrollResponder\n      ) {\n        const oldScrollViewTag = ReactNative.findNodeHandle(\n          this._refs[prevState.idx].getScrollResponder()\n        );\n        NativeModules.F8Scrolling.unpin(oldScrollViewTag);\n      }\n    }\n  }\n\n  handleSelectSegment(idx: number) {\n    if (this.state.idx !== idx) {\n      const { onSegmentChange } = this.props;\n      this.setState({ idx }, () => onSegmentChange && onSegmentChange(idx));\n    }\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.bianca\n  },\n  headerWrapper: {\n    android: {\n      // elevation: 2,\n      backgroundColor: \"transparent\",\n      // FIXME: elevation doesn't seem to work without setting border\n      borderRightWidth: 1,\n      marginRight: -1,\n      borderRightColor: \"transparent\"\n    }\n  },\n  listView: {\n    flex: 1,\n    backgroundColor: \"transparent\"\n  }\n});\n\nmodule.exports = ListContainer;\n"
  },
  {
    "path": "js/common/LoginButton.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, Alert } from \"react-native\";\nimport F8Button from \"./F8Button\";\nimport { logInWithFacebook } from \"../actions\";\nimport { connect } from \"react-redux\";\n\nclass LoginButton extends React.Component {\n  props: {\n    style: any,\n    source?: string, // For Analytics\n    dispatch: (action: any) => Promise<any>,\n    onLoggedIn: ?() => void\n  };\n  state: {\n    isLoading: boolean\n  };\n  _isMounted: boolean;\n\n  constructor() {\n    super();\n    this.state = { isLoading: false };\n  }\n\n  componentDidMount() {\n    this._isMounted = true;\n  }\n\n  componentWillUnmount() {\n    this._isMounted = false;\n  }\n\n  render() {\n    if (this.state.isLoading) {\n      return (\n        <F8Button\n          theme=\"fb\"\n          style={[styles.button, this.props.style]}\n          caption=\"Please wait...\"\n          onPress={() => {}}\n        />\n      );\n    }\n\n    return (\n      <F8Button\n        theme=\"fb\"\n        style={[styles.button, this.props.style]}\n        icon={require(\"./img/buttons/logo-fb.png\")}\n        caption=\"Continue with Facebook\"\n        fontSize={13}\n        onPress={() => this.logIn()}\n      />\n    );\n  }\n\n  async logIn() {\n    const { dispatch, onLoggedIn } = this.props;\n\n    this.setState({ isLoading: true });\n    try {\n      await Promise.all([dispatch(logInWithFacebook(this.props.source))]);\n    } catch (e) {\n      const message = e.message || e;\n      if (message !== \"Timed out\" && message !== \"Canceled by user\") {\n        Alert.alert(message);\n      }\n      return;\n    } finally {\n      this._isMounted && this.setState({ isLoading: false });\n    }\n\n    onLoggedIn && onLoggedIn();\n  }\n}\n\nconst styles = StyleSheet.create({\n  button: {\n    alignSelf: \"center\",\n    width: 284\n  }\n});\n\nmodule.exports = connect()(LoginButton);\n"
  },
  {
    "path": "js/common/MapView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\n/* Dependencies\n============================================================================= */\n\nimport React from \"react\";\nimport {\n  Platform,\n  View,\n  ScrollView,\n  Image,\n  PixelRatio,\n  InteractionManager\n} from \"react-native\";\nimport StyleSheet from \"./F8StyleSheet\";\nimport type { Map } from \"../reducers/maps\";\nimport PhotoView from \"react-native-photo-view\";\n\n/* =============================================================================\n<MapView />\n============================================================================= */\n\nclass MapView extends React.Component {\n  props: {\n    map: ?Map,\n    style?: any,\n    zoomable: boolean,\n    width: number,\n    height?: number\n  };\n\n  static defaultProps = {\n    zoomable: false\n  };\n\n  render() {\n    const { map } = this.props;\n    const mapRatio =\n      map && map.width && map.height ? map.width / map.height : 1;\n\n    if (this.props.zoomable) {\n      return (\n        <MapViewZoomable\n          map={map}\n          width={this.props.width}\n          height={this.props.height}\n          style={this.props.style}\n        />\n      );\n    } else {\n      return (\n        <MapViewDefault\n          map={map}\n          width={this.props.width}\n          height={this.props.width / mapRatio}\n          style={this.props.style}\n        />\n      );\n    }\n  }\n}\n\n/* =============================================================================\n<MapViewDefault />\n--------------------------------------------------------------------------------\nDefault display of 1-3x map images\n============================================================================= */\n\nclass MapViewDefault extends React.Component {\n  _isMounted: boolean;\n\n  state: {\n    loaded: boolean\n  };\n\n  constructor() {\n    super();\n    this.state = { loaded: false };\n    this._isMounted = false;\n  }\n\n  componentDidMount() {\n    this._isMounted = true;\n    InteractionManager.runAfterInteractions(() => {\n      this._isMounted && this.setState({ loaded: true });\n    });\n  }\n\n  componentWillUnmount() {\n    this._isMounted = false;\n  }\n\n  render() {\n    const { map, width, height } = this.props;\n\n    return this.state.loaded ? (\n      <Image\n        style={[styles.defaultMap, { width, height }, this.props.style]}\n        source={{ uri: urlForMap(map) }}\n      />\n    ) : null;\n  }\n}\n\n/* =============================================================================\n<MapViewZoomable />\n--------------------------------------------------------------------------------\nZoomable display of 1-3x map images\n============================================================================= */\n\nclass MapViewZoomable extends React.Component {\n  static defaultProps = {\n    maximumZoomScale: 2\n  };\n\n  render() {\n    const { map, width, height } = this.props;\n\n    if (Platform.OS === \"ios\") {\n      const paddingTop = 0;\n      const paddingBottom = 80;\n      const paddedHeight = height - paddingTop - paddingBottom;\n      const paddedWidth = paddedHeight * (map.width / map.height);\n\n      return (\n        <ScrollView\n          style={[\n            styles.zoomableContainer,\n            { width, height },\n            this.props.style\n          ]}\n          horizontal={true}\n          directionalLockEnabled={false}\n          scrollEventThrottle={100}\n          showsHorizontalScrollIndicator={false}\n          showsVerticalScrollIndicator={false}\n          maximumZoomScale={1.0001}\n          bouncesZoom={true}\n        >\n          <View style={{ paddingTop, paddingBottom }}>\n            <Image\n              style={{ width: paddedWidth, height: paddedHeight }}\n              source={{ uri: map.x3url }}\n            />\n          </View>\n        </ScrollView>\n      );\n    } else {\n      return (\n        <PhotoView\n          source={{ uri: map.x3url }}\n          maximumZoomScale={this.props.maximumZoomScale}\n          style={[{ width, height }, this.props.style]}\n        />\n      );\n    }\n  }\n}\n\n/* Get Map URL (1-3x) depending on device PixelRatio\n============================================================================= */\n\nfunction urlForMap(map: ?Map): string {\n  if (!map) {\n    return \"\";\n  }\n  switch (PixelRatio.get()) {\n    case 1:\n      return map.x1url;\n    case 2:\n      return map.x2url;\n    case 3:\n      return map.x3url;\n  }\n  return map.x3url;\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  defaultMap: {\n    resizeMode: \"contain\"\n  }\n});\n\n/* Export\n============================================================================= */\nmodule.exports = MapView;\n"
  },
  {
    "path": "js/common/MessengerChatHead.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, View, TouchableOpacity, Image } from \"react-native\";\nimport F8Linking from \"./F8Linking\";\nimport ProfilePicture from \"./ProfilePicture\";\nimport MessengerModal from \"./MessengerModal\";\n\n/* constants ================================================================ */\n\nconst LOGO_OFFSET_RIGHT = 5,\n  LOGO_OFFSET_BOTTOM = 7,\n  PROFILE_PICTURE_SIZE = 54,\n  CONTAINER_WIDTH = PROFILE_PICTURE_SIZE + LOGO_OFFSET_RIGHT,\n  CONTAINER_HEIGHT = PROFILE_PICTURE_SIZE + LOGO_OFFSET_BOTTOM;\n\n/* =============================================================================\n<MessengerChatHead />\n============================================================================= */\n\nclass MessengerChatHead extends React.Component {\n  constructor() {\n    super();\n\n    this.state = {\n      messengerModal: false\n    };\n  }\n\n  render() {\n    const { user } = this.props;\n    if (!user && user.id) {\n      return false;\n    } else {\n      return (\n        <View style={[styles.container, this.props.style]}>\n          <TouchableOpacity onPress={this.onPress}>\n            <ProfilePicture userID={user.id} size={PROFILE_PICTURE_SIZE} />\n            <Image\n              style={styles.logo}\n              source={require(\"./img/chathead-logo.png\")}\n            />\n          </TouchableOpacity>\n          <MessengerModal\n            visible={this.state.messengerModal}\n            dismiss={_ => this.setState({ messengerModal: false })}\n          />\n        </View>\n      );\n    }\n  }\n\n  onPress = _ => {\n    const { user } = this.props;\n    // If possible, try and link to friend's fb profile URL. If friend object\n    // has no profile link, go to m.me to contact them via Messenger instead\n    const userUrl = user.link || \"https://m.me\";\n    F8Linking.openURL(userUrl).catch(() =>\n      this.setState({ messengerModal: true })\n    );\n  };\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    width: CONTAINER_WIDTH,\n    height: CONTAINER_HEIGHT\n  },\n  logo: {\n    position: \"absolute\",\n    right: 0,\n    bottom: 0\n  }\n});\n\n/* export\n============================================================================= */\nexport default MessengerChatHead;\n"
  },
  {
    "path": "js/common/MessengerModal.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport F8Modal from \"./F8Modal\";\nimport F8Button from \"./F8Button\";\nimport F8Fonts from \"./F8Fonts\";\nimport { Platform, StyleSheet, View, Image, Alert } from \"react-native\";\nimport F8Linking from \"./F8Linking\";\nimport { Paragraph } from \"./F8Text\";\n\n/* constants ================================================================ */\n\nconst APP_STORE = \"https://itunes.apple.com/us/app/messenger/id454638411\",\n  PLAY_STORE =\n    \"https://play.google.com/store/apps/details?id=com.facebook.orca\";\n\n/* =============================================================================\n<MessengerModal />\n============================================================================= */\n\nclass MessengerModal extends React.Component {\n  static __cards__;\n\n  render() {\n    return (\n      <F8Modal\n        renderContent={this.renderContent}\n        renderFooter={this.renderFooter}\n        bottomGradient={[\n          F8Colors.colorWithAlpha(\"tangaroa\", 0),\n          F8Colors.colorWithAlpha(\"tangaroa\", 1)\n        ]}\n        {...this.props}\n      />\n    );\n  }\n\n  renderContent = () => {\n    return (\n      <View style={styles.content}>\n        <Image\n          style={styles.image}\n          source={require(\"./img/messenger-app-icon.png\")}\n        />\n        <Paragraph style={styles.text}>\n          <Paragraph style={styles.boldText}>\n            Install the Messenger app\n          </Paragraph>{\" \"}\n          to communicate with friends.\n        </Paragraph>\n        <F8Button\n          caption=\"Go to the App Store\"\n          onPress={_ => this.openAppStore()}\n        />\n      </View>\n    );\n  };\n\n  renderFooter = () => {\n    return (\n      <F8Button\n        style={{ marginBottom: 27 }}\n        theme=\"white\"\n        type=\"round\"\n        icon={require(\"./img/buttons/icon-x.png\")}\n        onPress={_ => this.props.dismiss && this.props.dismiss()}\n      />\n    );\n  };\n\n  openAppStore = () => {\n    const url = Platform.OS === \"ios\" ? APP_STORE : PLAY_STORE;\n    F8Linking.canOpenURL(url)\n      .then(supported => {\n        if (!supported) {\n          this.props.dismiss && this.props.dismiss();\n        } else {\n          return F8Linking.openURL(url);\n        }\n      })\n      .catch(() => {});\n  };\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  content: {\n    alignItems: \"center\",\n    paddingVertical: 45,\n    paddingHorizontal: 37\n  },\n  text: {\n    marginVertical: 20,\n    textAlign: \"center\"\n  },\n  boldText: {\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\")\n  }\n});\n\n/* Playground Cards ========================================================= */\n\nconst messengerModal = MessengerModal;\nmessengerModal.__cards__ = define => {\n  define(\"Default\", () => (\n    <MessengerModal visible={true} dismiss={_ => Alert.alert(\"Dismissed\")} />\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = messengerModal;\n"
  },
  {
    "path": "js/common/ParallaxBackground.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport Animated from \"Animated\";\nimport resolveAssetSource from \"resolveAssetSource\";\nimport React from \"react\";\nimport StyleSheet from \"StyleSheet\";\nimport Dimensions from \"Dimensions\";\n\n// TODO: Remove this magic numbers\nconst HEIGHT = Dimensions.get(\"window\").height > 600 ? 200 : 150;\nconst SCREEN_WIDTH = Dimensions.get(\"window\").width;\n\ntype Props = {\n  maxHeight: number,\n  minHeight: number,\n  offset: Animated.Value,\n  backgroundImage: number,\n  backgroundShift: number, // 0..1\n  backgroundColor: string, // TODO: This makes it seems like image loads faster. Remove\n  children?: any\n};\n\ntype State = {\n  shift: Animated.Value\n};\n\nclass ParallaxBackground extends React.Component {\n  props: Props;\n  state: State;\n\n  static HEIGHT = HEIGHT;\n\n  constructor(props: Props) {\n    super(props);\n    this.state = {\n      shift: new Animated.Value(props.backgroundShift || 0)\n    };\n  }\n\n  componentDidUpdate(prevProps: Props) {\n    if (prevProps.backgroundShift !== this.props.backgroundShift) {\n      Animated.timing(this.state.shift, {\n        toValue: this.props.backgroundShift,\n        duration: 300\n      }).start();\n    }\n  }\n\n  render(): ReactElement {\n    const { minHeight, maxHeight, offset, backgroundColor } = this.props;\n    const buffer = 10; // To reduce visual lag when scrolling\n    const height = offset.interpolate({\n      inputRange: [0, maxHeight - minHeight],\n      outputRange: [maxHeight + buffer, minHeight + buffer],\n      extrapolateRight: \"clamp\"\n    });\n\n    return (\n      <Animated.View style={[styles.container, { height, backgroundColor }]}>\n        {this.renderBackgroundImage()}\n        {this.renderContent()}\n      </Animated.View>\n    );\n  }\n\n  renderBackgroundImage(): ?ReactElement {\n    const { backgroundImage, minHeight, maxHeight, offset } = this.props;\n    if (!backgroundImage) {\n      return null;\n    }\n\n    const source = resolveAssetSource(backgroundImage);\n    if (!source) {\n      return null;\n    }\n    const { width } = source;\n    const translateX = this.state.shift.interpolate({\n      inputRange: [0, 1],\n      outputRange: [0, SCREEN_WIDTH - width],\n      extrapolate: \"clamp\"\n    });\n\n    const length = maxHeight - minHeight;\n    const translateY = offset.interpolate({\n      inputRange: [0, length / 2, length],\n      outputRange: [0, -length / 2, -length / 1.5],\n      extrapolate: \"clamp\"\n    });\n    // Sometimes image width is smaller than device's width\n    const initialScale = Math.max(SCREEN_WIDTH / width * 2 - 1, 1);\n    const scale = offset.interpolate({\n      inputRange: [-length, 0],\n      outputRange: [2, initialScale],\n      extrapolateRight: \"clamp\"\n    });\n    const transforms = {\n      transform: [{ translateX }, { translateY }, { scale }]\n    };\n    return <Animated.Image source={backgroundImage} style={transforms} />;\n  }\n\n  renderContent(): ?ReactElement {\n    if (React.Children.count(this.props.children) === 0) {\n      return null;\n    }\n    const content = React.Children.only(this.props.children);\n\n    const { minHeight, maxHeight, offset } = this.props;\n    const length = maxHeight - minHeight;\n    const opacity = offset.interpolate({\n      inputRange: [0, length - 40],\n      outputRange: [1, 0],\n      extrapolate: \"clamp\"\n    });\n    const translateY = offset.interpolate({\n      inputRange: [0, length],\n      outputRange: [-32, -(length / 2) - 32],\n      extrapolate: \"clamp\"\n    });\n    const transforms = { opacity, transform: [{ translateY }] };\n    return (\n      <Animated.View style={[styles.contentContainer, transforms]}>\n        {content}\n      </Animated.View>\n    );\n  }\n}\n\nconst HEADER_HEIGHT = HEIGHT + 156;\n\nconst styles = StyleSheet.create({\n  container: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    overflow: \"hidden\"\n  },\n  contentContainer: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    height: HEADER_HEIGHT,\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    backgroundColor: \"transparent\"\n  }\n});\n\nmodule.exports = ParallaxBackground;\n"
  },
  {
    "path": "js/common/PlayButton.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"./F8Colors\";\nimport { StyleSheet, View, TouchableOpacity, Image, Alert } from \"react-native\";\n\n/* constants ================================================================ */\n\nconst BUTTON_SIZE = 76,\n  BUTTON_SIZE_SM = 16;\n\n/**\n* ==============================================================================\n* <PlayButton />\n* ------------------------------------------------------------------------------\n* @param {?string} type Display style default \"large\" || \"small\"\n* @param {?string} buttonColor Background color\n* @param {?string} iconColor Icon tint color\n* @param {?function} onPress event callback\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass PlayButton extends React.Component {\n  static defaultProps = {\n    type: \"large\",\n    buttonColor: F8Colors.yellow,\n    iconColor: F8Colors.pink\n  };\n\n  render() {\n    // color theming (with defaults)\n    const buttonColorStyles = { backgroundColor: this.props.buttonColor };\n    const iconColorStyles = { tintColor: this.props.iconColor };\n    // size variation\n    const {\n      buttonSizeStyles,\n      iconSizeStyles,\n      iconImage\n    } = this.getSizeStyles();\n    // icon element\n    const image = (\n      <Image\n        style={[styles.icon, iconColorStyles, iconSizeStyles]}\n        source={iconImage}\n      />\n    );\n\n    // return TouchableOpacity container if there's an onPress handler else use a View\n    if (this.props.onPress) {\n      return (\n        <TouchableOpacity\n          onPress={this.onPress}\n          activeOpacity={0.8}\n          style={[styles.button, buttonColorStyles, buttonSizeStyles]}\n        >\n          {image}\n        </TouchableOpacity>\n      );\n    } else {\n      return (\n        <View style={[styles.button, buttonColorStyles, buttonSizeStyles]}>\n          {image}\n        </View>\n      );\n    }\n  }\n\n  onPress = _ => {\n    this.props.onPress && this.props.onPress();\n  };\n\n  getSizeStyles() {\n    const { type } = this.props;\n    let buttonSizeStyles, iconSizeStyles, iconImage;\n    if (type === \"small\") {\n      buttonSizeStyles = {\n        width: BUTTON_SIZE_SM,\n        height: BUTTON_SIZE_SM,\n        borderRadius: BUTTON_SIZE_SM / 2\n      };\n      iconSizeStyles = { transform: [{ translateX: 0.5 }] };\n      iconImage = require(\"./img/buttons/play-small.png\");\n    } else {\n      buttonSizeStyles = {\n        width: BUTTON_SIZE,\n        height: BUTTON_SIZE,\n        borderRadius: BUTTON_SIZE / 2\n      };\n      iconSizeStyles = { transform: [{ translateX: 4 }] };\n      iconImage = require(\"./img/buttons/play-large.png\");\n    }\n    return { buttonSizeStyles, iconSizeStyles, iconImage };\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  button: {\n    backgroundColor: \"black\",\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  icon: {\n    tintColor: \"white\"\n  }\n});\n\n/* playground cards ========================================================= */\n\nconst playButton = PlayButton;\nplayButton.__cards__ = define => {\n  define(\"Large Yellow/Pink (Default)\", _ => <PlayButton />);\n  define(\"Small Yellow/Pink\", _ => <PlayButton type=\"small\" />);\n\n  define(\"Large Blue/Green\", _ => (\n    <PlayButton\n      buttonColor={F8Colors.blue}\n      iconColor={F8Colors.green}\n      onPress={() => Alert.alert(\"<PlayButton /> pressed!\")}\n    />\n  ));\n\n  define(\"Small Pink/Yellow\", _ => (\n    <PlayButton\n      type=\"small\"\n      buttonColor={F8Colors.pink}\n      iconColor={F8Colors.yellow}\n      onPress={() => Alert.alert(\"<PlayButton /> pressed!\")}\n    />\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = playButton;\n"
  },
  {
    "path": "js/common/ProfilePicture.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Image, PixelRatio } from \"react-native\";\n\n/**\n* ==============================================================================\n* <ProfilePicture />\n* ------------------------------------------------------------------------------\n* @param {String} userID Facebook user ID\n* @param {Number} size The desired profile photo size\n* @return {ReactElement}\n* ==============================================================================\n*/\nclass ProfilePicture extends React.Component {\n  props: {\n    userID: string,\n    size: number\n  };\n\n  render() {\n    const { userID, size } = this.props;\n    const scaledSize = size * PixelRatio.get();\n    const graphURL = `https://graph.facebook.com/${userID}/picture?width=${scaledSize}&height=${scaledSize}`;\n    return (\n      <Image\n        source={{ uri: graphURL }}\n        style={{\n          width: size,\n          height: size,\n          borderRadius: size / 2\n        }}\n      />\n    );\n  }\n}\n\nmodule.exports = ProfilePicture;\n"
  },
  {
    "path": "js/common/PureListView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport ListView from \"ListView\";\nimport Platform from \"Platform\";\nimport React from \"react\";\n\ntype Rows = Array<Object>;\ntype RowsAndSections = {\n  [sectionID: string]: Object\n};\n\nexport type Data = Rows | RowsAndSections;\ntype RenderElement = () => ?ReactElement;\n\ntype Props = {\n  data: Data,\n  renderEmptyList?: ?RenderElement,\n  minContentHeight: number,\n  contentInset: { top: number, bottom: number }\n};\n\ntype State = {\n  contentHeight: number,\n  dataSource: ListView.DataSource\n};\n\n// FIXME: Android has a bug when scrolling ListView the view insertions\n// will make it go reverse. Temporary fix - pre-render more rows\nconst LIST_VIEW_PAGE_SIZE = Platform.OS === \"android\" ? 20 : 10;\n\nclass PureListView extends React.Component {\n  props: Props;\n  state: State;\n\n  static defaultProps = {\n    data: [],\n    contentInset: { top: 0, bottom: 0 },\n    // TODO: This has to be scrollview height + fake header\n    minContentHeight: 0\n    // renderSeparator: (sectionID, rowID) => <View style={styles.separator} key={rowID} />,\n  };\n\n  constructor(props: Props) {\n    super(props);\n    let dataSource = new ListView.DataSource({\n      getRowData: (dataBlob, sid, rid) => dataBlob[sid][rid],\n      getSectionHeaderData: (dataBlob, sid) => dataBlob[sid],\n      rowHasChanged: (row1, row2) => row1 !== row2,\n      sectionHeaderHasChanged: (s1, s2) => s1 !== s2\n    });\n\n    this.state = {\n      contentHeight: 0,\n      containerHeight: 0,\n      dataSource: cloneWithData(dataSource, props.data)\n    };\n\n    (this: any).renderFooter = this.renderFooter.bind(this);\n    (this: any).renderHeader = this.renderHeader.bind(this);\n    (this: any).onContentSizeChange = this.onContentSizeChange.bind(this);\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    if (this.props.data !== nextProps.data) {\n      this.setState({\n        dataSource: cloneWithData(this.state.dataSource, nextProps.data)\n      });\n    }\n  }\n\n  render() {\n    const { contentInset } = this.props;\n    const bottom =\n      contentInset.bottom +\n      Math.max(0, this.props.minContentHeight - this.state.contentHeight);\n    return (\n      <ListView\n        initialListSize={10}\n        pageSize={LIST_VIEW_PAGE_SIZE}\n        enableEmptySections={true}\n        removeClippedSubviews={false}\n        {...this.props}\n        ref={c => (this._listview = c)}\n        dataSource={this.state.dataSource}\n        renderHeader={this.renderHeader}\n        renderFooter={this.renderFooter}\n        contentInset={{ bottom, top: contentInset.top }}\n        onContentSizeChange={this.onContentSizeChange}\n        onLayout={event => {\n          const { height } = event.nativeEvent.layout;\n          if (this.state.containerHeight !== height) {\n            this.setState({ containerHeight: height });\n          }\n        }}\n      />\n    );\n  }\n\n  onContentSizeChange(contentWidth: number, contentHeight: number) {\n    if (contentHeight !== this.state.contentHeight) {\n      this.setState({ contentHeight });\n    }\n  }\n\n  scrollTo(...args: Array<any>) {\n    this._listview.scrollTo(...args);\n  }\n\n  getScrollResponder(): any {\n    return this._listview.getScrollResponder();\n  }\n\n  renderHeader(): ?ReactElement {\n    if (this.state.dataSource.getRowCount() !== 0) {\n      return this.props.renderHeader && this.props.renderHeader();\n    }\n  }\n\n  renderFooter(): ?ReactElement {\n    if (this.state.dataSource.getRowCount() === 0) {\n      return (\n        this.props.renderEmptyList &&\n        this.props.renderEmptyList(this.state.containerHeight)\n      );\n    }\n\n    return this.props.renderFooter && this.props.renderFooter();\n  }\n}\n\nfunction cloneWithData(dataSource: ListView.DataSource, data: ?Data) {\n  if (!data) {\n    return dataSource.cloneWithRows([]);\n  }\n  if (Array.isArray(data)) {\n    return dataSource.cloneWithRows(data);\n  }\n  return dataSource.cloneWithRowsAndSections(data);\n}\n\nmodule.exports = PureListView;\n"
  },
  {
    "path": "js/common/ViewPager.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport {\n  View,\n  StyleSheet,\n  ScrollView,\n  ViewPagerAndroid,\n  Platform\n} from \"react-native\";\n\ntype Props = {\n  count: number,\n  selectedIndex: number,\n  onSelectedIndexChange?: (index: number) => void,\n  bounces?: boolean,\n  children?: any,\n  style?: any\n};\n\ntype State = {\n  width: number,\n  height: number,\n  selectedIndex: number,\n  initialSelectedIndex: number,\n  scrollingTo: ?number\n};\n\nclass ViewPager extends React.Component {\n  props: Props;\n  state: State;\n\n  constructor(props: Props) {\n    super(props);\n    this.state = {\n      width: 0,\n      height: 0,\n      selectedIndex: this.props.selectedIndex,\n      initialSelectedIndex: this.props.selectedIndex,\n      scrollingTo: null\n    };\n    (this: any).handleHorizontalScroll = this.handleHorizontalScroll.bind(this);\n    (this: any).adjustCardSize = this.adjustCardSize.bind(this);\n  }\n\n  render() {\n    if (Platform.OS === \"ios\") {\n      return this.renderIOS();\n    } else {\n      return this.renderAndroid();\n    }\n  }\n\n  renderIOS() {\n    return (\n      <ScrollView\n        ref={c => (this._scrollview = c)}\n        contentOffset={{\n          x: this.state.width * this.state.initialSelectedIndex,\n          y: 0\n        }}\n        style={[styles.scrollview, this.props.style]}\n        horizontal={true}\n        pagingEnabled={true}\n        bounces={!!this.props.bounces}\n        scrollsToTop={false}\n        onScroll={this.handleHorizontalScroll}\n        scrollEventThrottle={100}\n        removeClippedSubviews={false}\n        automaticallyAdjustContentInsets={false}\n        directionalLockEnabled={true}\n        showsHorizontalScrollIndicator={false}\n        showsVerticalScrollIndicator={false}\n        onLayout={this.adjustCardSize}\n      >\n        {this.renderContent()}\n      </ScrollView>\n    );\n  }\n\n  renderAndroid() {\n    return (\n      <ViewPagerAndroid\n        ref={c => (this._scrollview = c)}\n        initialPage={this.state.initialSelectedIndex}\n        onPageSelected={this.handleHorizontalScroll}\n        style={styles.container}\n      >\n        {this.renderContent()}\n      </ViewPagerAndroid>\n    );\n  }\n\n  adjustCardSize(e: any) {\n    this.setState({\n      width: e.nativeEvent.layout.width,\n      height: e.nativeEvent.layout.height\n    });\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    if (nextProps.selectedIndex !== this.state.selectedIndex) {\n      if (Platform.OS === \"ios\") {\n        this._scrollview.scrollTo({\n          x: nextProps.selectedIndex * this.state.width,\n          animated: true\n        });\n        this.setState({ scrollingTo: nextProps.selectedIndex });\n      } else {\n        this._scrollview.setPage(nextProps.selectedIndex);\n        this.setState({ selectedIndex: nextProps.selectedIndex });\n      }\n    }\n  }\n\n  renderContent(): Array<ReactElement> {\n    const { width, height } = this.state;\n    const style = Platform.OS === \"ios\" && styles.card;\n    return React.Children.map(this.props.children, (child, i) => (\n      <View style={[style, { width, height }]} key={\"r_\" + i}>\n        {child}\n      </View>\n    ));\n  }\n\n  handleHorizontalScroll(e: any) {\n    let selectedIndex = e.nativeEvent.position;\n    if (selectedIndex === undefined) {\n      selectedIndex = Math.round(\n        e.nativeEvent.contentOffset.x / this.state.width\n      );\n    }\n    if (selectedIndex < 0 || selectedIndex >= this.props.count) {\n      return;\n    }\n    if (\n      this.state.scrollingTo !== null &&\n      this.state.scrollingTo !== selectedIndex\n    ) {\n      return;\n    }\n    if (\n      this.props.selectedIndex !== selectedIndex ||\n      this.state.scrollingTo !== null\n    ) {\n      this.setState({ selectedIndex, scrollingTo: null }, () => {\n        // the onSelectedIndexChange handler can change props.selectedIndex, so we want\n        // to call it after the state has actually changed to avoid extra scrollTo call\n        // (see componentWillReceiveProps)\n        const { onSelectedIndexChange } = this.props;\n        onSelectedIndexChange && onSelectedIndexChange(selectedIndex);\n      });\n    }\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1\n  },\n  scrollview: {\n    flex: 1,\n    backgroundColor: \"transparent\"\n  },\n  card: {\n    backgroundColor: \"transparent\"\n  }\n});\n\nmodule.exports = ViewPager;\n"
  },
  {
    "path": "js/common/__tests__/convertTimes-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\njest.autoMockOff();\nimport {\n  parseTimeToUTC,\n  sessionsHappeningNow,\n  sessionsHappeningToday\n} from \"../convertTimes\";\n\n// This corrects for times in Parse being inputted as local pacific time\n// but the timestamp being provided as a unix time epoch\ndescribe(\"parseTimeToUTC\", () => {\n  const pacificDiffMs = 1000 * 60 * 60 * 7;\n  const random = 1491352767750;\n  const day1StartUTC = 1492473600000;\n  const day2StartUTC = 1492560000000;\n\n  it(\"corrects Parse time from UTC to local pacific time\", () => {\n    expect(parseTimeToUTC(random)).toEqual(random + pacificDiffMs);\n    expect(parseTimeToUTC(day1StartUTC)).toEqual(day1StartUTC + pacificDiffMs);\n    expect(parseTimeToUTC(day2StartUTC)).toEqual(day2StartUTC + pacificDiffMs);\n  });\n});\n\n// Whether the provided timestamp is within the range of session start and\n// end times\ndescribe(\"sessionsHappeningNow\", () => {\n  const random = 1491352767750;\n  const mockSessionsBasic = [\n    { startTime: 1491352767600, endTime: 1491352767601 },\n    { startTime: 1491352767800, endTime: 1491352767801 }\n  ];\n  const mockSessionsReversed = [\n    { startTime: 1491352767800, endTime: 1491352767801 },\n    { startTime: 1491352767600, endTime: 1491352767601 }\n  ];\n  const mockSessionsWrong = [\n    { startTime: 1491252767800, endTime: 1491252767801 },\n    { startTime: 1491252767600, endTime: 1491252767601 }\n  ];\n\n  it(\"returns false for empty sessions\", () => {\n    expect(sessionsHappeningNow(random, [])).toEqual(false);\n  });\n\n  it(\"returns true if time is within range\", () => {\n    expect(sessionsHappeningNow(random, mockSessionsBasic)).toEqual(true);\n    expect(sessionsHappeningNow(random, mockSessionsReversed)).toEqual(true);\n  });\n\n  it(\"returns false if time is outside of range\", () => {\n    expect(sessionsHappeningNow(random, mockSessionsWrong)).toEqual(false);\n  });\n});\n\n// For a given timestamp return a boolean if that time is within either day 1\n// or day 2 of the conference (for time-based functionality like hide completed)\ndescribe(\"sessionsHappeningToday\", () => {\n  const day0Start = \"Mon Apr 17 2017 00:00:00 GMT-0700 (PDT)\", // 1492412400000\n    day0End = \"Mon Apr 17 2017 23:59:59 GMT-0700 (PDT)\", // 1492498799000\n    day1Start = \"Tue Apr 18 2017 00:00:00 GMT-0700 (PDT)\", // 1492498800000\n    day1Middle = \"Tue Apr 18 2017 12:00:00 GMT-0700 (PDT)\", // 1492542000000\n    day1End = \"Tue Apr 18 2017 23:59:59 GMT-0700 (PDT)\", // 1492585199000\n    day2Start = \"Wed Apr 19 2017 00:00:00 GMT-0700 (PDT)\", // 1492585200000\n    day2Middle = \"Wed Apr 19 2017 12:00:00 GMT-0700 (PDT)\", // 1492628400000\n    day2End = \"Wed Apr 19 2017 23:59:59 GMT-0700 (PDT)\", // 1492671599000\n    day3Start = \"Thu Apr 20 2017 00:00:00 GMT-0700 (PDT)\", // 1492671600000\n    day3End = \"Thu Apr 20 2017 23:59:59 GMT-0700 (PDT)\"; // 1492757999000\n\n  it(\"returns false before the conference\", () => {\n    // the day before\n    expect(sessionsHappeningToday(new Date(day0Start).getTime())).toEqual(\n      false\n    );\n    expect(sessionsHappeningToday(new Date(day0End).getTime())).toEqual(false);\n  });\n\n  it(\"returns true during the conference\", () => {\n    // during (day 1)\n    expect(sessionsHappeningToday(new Date(day1Start).getTime())).toEqual(true);\n    expect(sessionsHappeningToday(new Date(day1Middle).getTime())).toEqual(\n      true\n    );\n    expect(sessionsHappeningToday(new Date(day1End).getTime())).toEqual(true);\n    // during (day 2)\n    expect(sessionsHappeningToday(new Date(day2Start).getTime())).toEqual(true);\n    expect(sessionsHappeningToday(new Date(day2Middle).getTime())).toEqual(\n      true\n    );\n    expect(sessionsHappeningToday(new Date(day2End).getTime())).toEqual(true);\n  });\n\n  it(\"returns false after the conference\", () => {\n    // the day after\n    expect(sessionsHappeningToday(new Date(day3Start).getTime())).toEqual(\n      false\n    );\n    expect(sessionsHappeningToday(new Date(day3End).getTime())).toEqual(false);\n  });\n});\n"
  },
  {
    "path": "js/common/convertTimes.js",
    "content": "import moment from \"moment-timezone\";\nimport { timezone } from \"../env.js\";\n\nimport type { Session } from \"../reducers/sessions\";\n\nconst CONFERENCE_DATES = [1492473600000, 1492560000000];\n\nexport function parseTimeToUTC(unix: number): number {\n  const offset = moment.tz.zone(timezone).offset(unix);\n  const utc = moment.utc(unix);\n  const fixed = utc.clone().add(offset, \"minutes\");\n  return fixed.valueOf();\n}\n\nfunction minutesSinceMidnight(): number {\n  const now = moment().tz(timezone);\n  const dayStart = now.clone().startOf(\"day\");\n  return now.diff(dayStart, \"minutes\");\n}\n\nexport function currentTimeOnConferenceDay(day: number = 1): number {\n  const utc = moment.utc(parseTimeToUTC(CONFERENCE_DATES[day - 1]));\n  const mappedTime = utc.clone().add(minutesSinceMidnight(), \"minutes\");\n  return mappedTime.valueOf();\n}\n\nexport function sessionsHappeningNow(\n  now: number,\n  sessions: Array<Session> = []\n): boolean {\n  if (sessions.length) {\n    let start, end;\n    sessions.map(session => {\n      if (!start || start > session.startTime) {\n        start = session.startTime;\n      }\n      if (!end || end < session.endTime) {\n        end = session.startTime;\n      }\n    });\n    return now >= start && now <= end;\n  } else {\n    return false;\n  }\n}\n\nexport function sessionsHappeningToday(now: number): boolean {\n  const day1Start = new Date(\n    \"Tue Apr 18 2017 00:00:00 GMT-0700 (PDT)\"\n  ).getTime();\n  const day2End = new Date(\"Wed Apr 19 2017 23:59:59 GMT-0700 (PDT)\").getTime();\n  return now >= day1Start && now <= day2End;\n}\n"
  },
  {
    "path": "js/env.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nmodule.exports = {\n  version: 410,\n  testMenuEnabled: false,\n  parseAppID: \"oss-f8-app-2017\",\n  serverURL: \"http://localhost:1337\",\n  graphqlURL: \"http://localhost:4000/graphql\",\n  compatibleStoreVersion: \"0.10\",\n  gcmSenderId: \"336769939688\",\n  timezone: \"America/Los_Angeles\",\n  dayLabel(num) {\n    const days = { 1: \"Tue 4/18\", 2: \"Wed 4/19\" };\n    return days[num] || `Day ${num}`;\n  }\n};\n"
  },
  {
    "path": "js/filter/FilterScreen.android.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport TopicItem from \"./TopicItem\";\nimport ItemsWithSeparator from \"../common/ItemsWithSeparator\";\nimport {\n  Animated,\n  View,\n  ScrollView,\n  Modal,\n  TouchableOpacity\n} from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport Hitbox from \"../common/Hitbox\";\nimport { HeaderTitle, Heading4 } from \"../common/F8Text\";\nimport { connect } from \"react-redux\";\n\nconst DRAWER_WIDTH = 300;\n\nclass FilterScreen extends React.Component {\n  props: {\n    isLoggedIn: boolean,\n    topics: Array<string>,\n    selectedTopics: { [id: string]: boolean },\n    dispatch: (action: any) => void,\n    navigator: any,\n    onClose: ?() => void\n  };\n  state: {\n    selectedTopics: { [id: string]: boolean },\n    anim: Animated.Value\n  };\n\n  constructor(props) {\n    super(props);\n    this.state = {\n      selectedTopics: { ...this.props.selectedTopics },\n      anim: new Animated.Value(0)\n    };\n\n    (this: any).clearFilter = this.clearFilter.bind(this);\n    (this: any).close = this.close.bind(this);\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (this.props.selectedTopics !== nextProps.selectedTopics) {\n      this.setState({ selectedTopics: { ...nextProps.selectedTopics } });\n    }\n  }\n\n  componentWillUpdate(nextProps, nextState) {\n    if (this.props.visble !== nextProps.visible && nextProps.visible) {\n      setTimeout(_ => {\n        this.showDrawer(true);\n      }, 250);\n    }\n  }\n\n  render() {\n    return (\n      <Modal visible={this.props.visible} transparent={true}>\n        <Hitbox\n          onPress={this.close}\n          style={{ backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.5) }}\n        />\n        <View style={styles.contentWrapper}>\n          <Animated.View\n            style={[\n              styles.contentDrawer,\n              {\n                transform: [\n                  {\n                    translateX: this.state.anim.interpolate({\n                      inputRange: [0, 1],\n                      outputRange: [0, DRAWER_WIDTH]\n                    })\n                  }\n                ]\n              }\n            ]}\n          >\n            {this.renderHeader()}\n            <ScrollView\n              showsVerticalScrollIndicator={false}\n              contentContainerStyle={styles.scrollview}\n            >\n              {this.renderTopics()}\n            </ScrollView>\n          </Animated.View>\n        </View>\n      </Modal>\n    );\n  }\n\n  renderHeader() {\n    let clearButton;\n    if (this.hasSelectedTopics()) {\n      clearButton = (\n        <TouchableOpacity style={{ flex: 0 }} onPress={this.clearFilter}>\n          <Heading4 style={{ color: \"rgba(106,148,230,1)\" }}>CLEAR</Heading4>\n        </TouchableOpacity>\n      );\n    }\n\n    return (\n      <View style={styles.header}>\n        <HeaderTitle style={{ color: F8Colors.pink, flex: 1 }}>\n          Filter\n        </HeaderTitle>\n        {clearButton}\n      </View>\n    );\n  }\n\n  renderTopics() {\n    const topics = this.props.topics.map((topic, idx) => {\n      return (\n        <TopicItem\n          key={topic}\n          topic={topic}\n          icon={idx}\n          isChecked={this.state.selectedTopics[topic]}\n          onToggle={this.toggleTopic.bind(this, topic)}\n        />\n      );\n    });\n    return (\n      <ItemsWithSeparator separatorStyle={styles.separator}>\n        {topics}\n      </ItemsWithSeparator>\n    );\n  }\n\n  toggleTopic(topic) {\n    const selectedTopics = { ...this.state.selectedTopics };\n    let value = !selectedTopics[topic];\n    if (value) {\n      selectedTopics[topic] = true;\n    } else {\n      delete selectedTopics[topic];\n    }\n    this.applyFilter(selectedTopics);\n  }\n\n  applyFilter(selectedTopics) {\n    this.setState({ selectedTopics });\n    this.props.onApply && this.props.onApply(selectedTopics);\n  }\n\n  close() {\n    this.showDrawer(false);\n    setTimeout(_ => {\n      this.props.onClose && this.props.onClose();\n    }, 250);\n  }\n\n  clearFilter() {\n    this.applyFilter({});\n  }\n\n  hasSelectedTopics() {\n    return this.props.topics.some(topic => this.state.selectedTopics[topic]);\n  }\n\n  showDrawer(visible) {\n    const toValue = visible ? 0 : 1;\n    const duration = visible ? 250 : 250;\n    Animated.timing(this.state.anim, { toValue, duration }).start();\n  }\n}\n\nconst styles = StyleSheet.create({\n  contentWrapper: {\n    flex: 1,\n    alignItems: \"flex-end\"\n  },\n  header: {\n    height: 65,\n    alignItems: \"center\",\n    flexDirection: \"row\",\n    paddingHorizontal: 23\n  },\n  contentDrawer: {\n    flex: 1,\n    width: DRAWER_WIDTH,\n    backgroundColor: F8Colors.tangaroa\n  },\n  scrollview: {},\n  separator: {\n    backgroundColor: \"rgba(20, 38, 74, 1)\"\n  }\n});\n\nmodule.exports = connect()(FilterScreen);\n"
  },
  {
    "path": "js/filter/FilterScreen.ios.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Header from \"../common/F8Header\";\nimport F8Colors from \"../common/F8Colors\";\nimport TopicItem from \"./TopicItem\";\nimport F8Button from \"../common/F8Button\";\nimport ItemsWithSeparator from \"../common/ItemsWithSeparator\";\nimport { View, ScrollView } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport ActionsOverlay from \"../common/ActionsOverlay\";\n\nimport shallowEqual from \"fbjs/lib/shallowEqual\";\nimport { connect } from \"react-redux\";\n\nclass FilterScreen extends React.Component {\n  props: {\n    topics: Array<string>,\n    selectedTopics: { [id: string]: boolean },\n    dispatch: (action: any) => void,\n    navigator: any,\n    onClose: ?() => void\n  };\n  state: {\n    selectedTopics: { [id: string]: boolean }\n  };\n\n  static defaultProps = {\n    topics: [],\n    selectedTopics: {}\n  };\n\n  constructor(props) {\n    super(props);\n    this.state = {\n      selectedTopics: { ...this.props.selectedTopics }\n    };\n\n    (this: any).applyFilter = this.applyFilter.bind(this);\n    (this: any).clearFilter = this.clearFilter.bind(this);\n    (this: any).close = this.close.bind(this);\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (this.props.selectedTopics !== nextProps.selectedTopics) {\n      this.setState({ selectedTopics: { ...nextProps.selectedTopics } });\n    }\n  }\n\n  componentWillUpdate(nextProps, nextState) {\n    if (this.state.selectedTopics !== nextState.selectedTopics) {\n      const applyButtonEnabled = !shallowEqual(\n        nextProps.selectedTopics,\n        nextState.selectedTopics\n      );\n      this.setState({ applyButtonEnabled });\n    }\n  }\n\n  render() {\n    let rightItem;\n    if (this.hasSelectedTopics()) {\n      rightItem = {\n        title: \"Clear\",\n        icon: require(\"../common/img/x-white.png\"),\n        onPress: this.clearFilter\n      };\n    }\n\n    return (\n      <View style={styles.container}>\n        <F8Header\n          title=\"Filter\"\n          backgroundColor={F8Colors.tangaroa}\n          titleColor={F8Colors.pink}\n          rightItem={rightItem}\n        />\n        <ScrollView\n          showsVerticalScrollIndicator={false}\n          contentContainerStyle={styles.scrollview}\n        >\n          {this.renderTopics()}\n        </ScrollView>\n        {this.renderActions()}\n      </View>\n    );\n  }\n\n  renderTopics() {\n    const topics = this.props.topics.map((topic, idx) => {\n      return (\n        <TopicItem\n          key={topic}\n          topic={topic}\n          icon={idx}\n          isChecked={this.state.selectedTopics[topic]}\n          onToggle={this.toggleTopic.bind(this, topic)}\n        />\n      );\n    });\n    return (\n      <ItemsWithSeparator separatorStyle={styles.separator}>\n        {topics}\n      </ItemsWithSeparator>\n    );\n  }\n\n  renderActions() {\n    let applyButton;\n    if (this.state.applyButtonEnabled) {\n      applyButton = (\n        <F8Button\n          style={{ flex: 1 }}\n          caption=\"Apply\"\n          onPress={this.applyFilter}\n        />\n      );\n    } else {\n      applyButton = (\n        <F8Button theme=\"disabled\" style={{ flex: 1 }} caption=\"Apply\" />\n      );\n    }\n\n    return (\n      <ActionsOverlay style={styles.actions}>\n        {applyButton}\n        <F8Button\n          theme=\"white\"\n          type=\"round\"\n          style={{ marginLeft: 7 }}\n          icon={require(\"../common/img/buttons/icon-x.png\")}\n          onPress={this.close}\n        />\n      </ActionsOverlay>\n    );\n  }\n\n  toggleTopic(topic) {\n    const selectedTopics = { ...this.state.selectedTopics };\n    const value = !selectedTopics[topic];\n    if (value) {\n      selectedTopics[topic] = true;\n    } else {\n      delete selectedTopics[topic];\n    }\n    this.setState({ selectedTopics });\n  }\n\n  applyFilter() {\n    this.props.onApply && this.props.onApply(this.state.selectedTopics);\n    this.close();\n  }\n\n  close() {\n    const { navigator, onClose } = this.props;\n    if (navigator) {\n      requestAnimationFrame(() => navigator.pop());\n    }\n    if (onClose) {\n      onClose();\n    }\n  }\n\n  clearFilter() {\n    this.setState({ selectedTopics: {} });\n  }\n\n  hasSelectedTopics() {\n    return this.props.topics.some(topic => this.state.selectedTopics[topic]);\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.tangaroa\n  },\n  scrollview: {\n    ios: {\n      paddingTop: 20,\n      paddingBottom: ActionsOverlay.height\n    }\n  },\n  separator: {\n    backgroundColor: \"rgba(20, 38, 74, 1)\"\n  },\n  actions: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0\n    // alignItems: 'stretch'\n  }\n});\n\nmodule.exports = connect()(FilterScreen);\n"
  },
  {
    "path": "js/filter/FriendsList.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react-native\";\nconst { Image, StyleSheet, Text, TouchableOpacity, View } = React;\n\ntype Friend = {\n  id: string,\n  name: string\n};\n\nclass FriendsList extends React.Component {\n  props: {\n    friends: Array<Friend>,\n    onPress: (friend: Friend) => void\n  };\n\n  render() {\n    if (this.props.friends.length === 0) {\n      return (\n        <View style={[styles.container, styles.noFriends]}>\n          <Text style={styles.text}>\n            No friends have shared their schedule.\n          </Text>\n        </View>\n      );\n    }\n    return (\n      <View style={styles.container}>\n        {this.props.friends.map(friend => (\n          <UserPog\n            user={friend}\n            key={friend.id}\n            onPress={() => this.props.onPress(friend)}\n          />\n        ))}\n      </View>\n    );\n  }\n}\n\nclass UserPog extends React.Component {\n  props: {\n    user: Friend,\n    onPress: () => void\n  };\n\n  render() {\n    const { id, name } = this.props.user;\n    const firstName = name.split(\" \")[0]; // TODO: problems with i18n\n    return (\n      <TouchableOpacity style={styles.pog} onPress={this.props.onPress}>\n        <Image\n          style={styles.profilePic}\n          source={{ uri: `https://graph.facebook.com/${id}/picture` }}\n        />\n        <Text style={styles.text}>{firstName}</Text>\n      </TouchableOpacity>\n    );\n  }\n}\n\nconst SIZE = 50;\n\nconst styles = StyleSheet.create({\n  container: {\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  noFriends: {\n    height: SIZE,\n    borderRadius: SIZE / 2,\n    backgroundColor: \"rgba(3, 34, 80, 0.15)\"\n  },\n  pog: {\n    alignItems: \"center\",\n    margin: 6\n  },\n  profilePic: {\n    marginBottom: 6,\n    width: SIZE,\n    height: SIZE,\n    borderRadius: SIZE / 2\n  },\n  text: {\n    color: \"white\",\n    fontSize: 12,\n    textAlign: \"center\"\n  }\n});\n\nmodule.exports = FriendsList;\n"
  },
  {
    "path": "js/filter/Header.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport StyleSheet from \"StyleSheet\";\nimport { Text } from \"../F8Text\";\nimport TouchableOpacity from \"TouchableOpacity\";\nimport View from \"View\";\n\nclass Header extends React.Component {\n  render() {\n    return (\n      <View style={[styles.header, this.props.style]}>\n        <View style={styles.leftItem}>\n          {this.renderItem(\n            this.props.leftItemTitle,\n            this.props.onLeftItemPress\n          )}\n        </View>\n        <View style={styles.centerItem}>\n          <Text style={[styles.titleText, this.props.titleStyle]}>\n            {this.props.title}\n          </Text>\n        </View>\n        <View style={styles.rightItem}>\n          {this.renderItem(\n            this.props.rightItemTitle,\n            this.props.onRightItemPress\n          )}\n        </View>\n      </View>\n    );\n  }\n\n  renderItem(title: string, onPress: () => void) {\n    if (!title) {\n      return null;\n    }\n\n    return (\n      <TouchableOpacity onPress={onPress} style={styles.itemWrapper}>\n        <Text style={styles.itemTitle}>{title.toUpperCase()}</Text>\n      </TouchableOpacity>\n    );\n  }\n}\n\nconst STATUS_BAR_HEIGHT = 20;\nconst HEADER_HEIGHT = STATUS_BAR_HEIGHT + /* toolbar */ 44;\n\nconst styles = StyleSheet.create({\n  header: {\n    backgroundColor: \"transparent\",\n    paddingTop: STATUS_BAR_HEIGHT,\n    height: HEADER_HEIGHT,\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n    alignItems: \"center\"\n  },\n  titleText: {\n    color: \"white\",\n    fontSize: 17\n  },\n  leftItem: {\n    flex: 1,\n    alignItems: \"flex-start\"\n  },\n  centerItem: {\n    flex: 2,\n    alignItems: \"center\"\n  },\n  rightItem: {\n    flex: 1,\n    alignItems: \"flex-end\"\n  },\n  itemWrapper: {\n    padding: 11\n  },\n  itemTitle: {\n    letterSpacing: 1,\n    fontSize: 12,\n    color: \"white\"\n  }\n});\n\nmodule.exports = Header;\n"
  },
  {
    "path": "js/filter/Section.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, View } from \"react-native\";\nimport { Text } from \"../common/F8Text\";\n\nclass Section extends React.Component {\n  render() {\n    const { children, title } = this.props;\n    if (React.Children.count(children) === 0) {\n      return null;\n    }\n    return (\n      <View style={styles.container}>\n        <Text style={styles.title}>{title.toUpperCase()}</Text>\n        {children}\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    marginBottom: 50\n  },\n  title: {\n    fontSize: 12,\n    letterSpacing: 1,\n    color: \"#A0B7FF\",\n    textAlign: \"center\",\n    margin: 10\n  }\n});\n\nmodule.exports = Section;\n"
  },
  {
    "path": "js/filter/TopicItem.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, TouchableOpacity, Image } from \"react-native\";\nimport { Text } from \"../common/F8Text\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport * as TopicIcons from \"./topicIcons\";\n\n/**\n* ==============================================================================\n* Filter screen list items\n* ------------------------------------------------------------------------------\n* @extends React.Component\n* @param {string} topic The name of the topic\n* @param {number} icon the year to match against\n* @param {boolean} isChecked whether to show the default/active appearance\n* @param {function} onToggle handler for tap event\n* ==============================================================================\n*/\nexport default class TopicItem extends React.Component {\n  props: {\n    topic: string,\n    icon: number,\n    isChecked: boolean,\n    onToggle: (value: boolean) => void\n  };\n\n  render() {\n    const { topic, icon, isChecked, onToggle } = this.props;\n\n    const activeIcon = TopicIcons.get(\"active\", icon);\n    const defaultIcon = TopicIcons.get(\"default\", icon);\n    const accessibilityTraits = [\"button\"];\n    if (isChecked) {\n      accessibilityTraits.push(\"selected\");\n    }\n\n    return (\n      <TouchableOpacity\n        accessibilityTraits={accessibilityTraits}\n        activeOpacity={0.8}\n        style={styles.container}\n        onPress={onToggle}\n      >\n        <Image\n          style={styles.icon}\n          source={isChecked ? activeIcon : defaultIcon}\n        />\n        <Text style={styles.title}>{topic}</Text>\n      </TouchableOpacity>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  container: {\n    paddingVertical: 17,\n    paddingHorizontal: 13,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"space-between\"\n  },\n  title: {\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"offWhite\"),\n    fontSize: 17,\n    color: \"white\",\n    flex: 1\n  },\n  icon: {\n    marginRight: 18\n  }\n});\n"
  },
  {
    "path": "js/filter/topicIcons.js",
    "content": "const TOPIC_ICONS = {\n  default: [require(\"./img/default/1.png\")],\n  active: [\n    require(\"./img/active/1.png\"),\n    require(\"./img/active/2.png\"),\n    require(\"./img/active/3.png\"),\n    require(\"./img/active/4.png\"),\n    require(\"./img/active/5.png\"),\n    require(\"./img/active/6.png\"),\n    require(\"./img/active/7.png\"),\n    require(\"./img/active/8.png\"),\n    require(\"./img/active/9.png\"),\n    require(\"./img/active/10.png\")\n  ]\n};\n\n/**\n* ==============================================================================\n* Get filter list item icons by index and active/default state\n* ------------------------------------------------------------------------------\n* @param {?string} state Whether to return default or active state icons\n* @param {?number} idx Index for varied icons\n* @return {number} RN Asset Source format\n* ==============================================================================\n*/\nexport function get(state = \"default\", idx = 0) {\n  // sanitize input\n  if (!TOPIC_ICONS[state]) {\n    return TOPIC_ICONS.default[0];\n  }\n  // map provided index to the number of available options\n  const mappedIndex = (idx + 1) % TOPIC_ICONS[state].length;\n  return TOPIC_ICONS[state][mappedIndex];\n}\n"
  },
  {
    "path": "js/flow-lib.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\n/* eslint no-unused-vars:0 */\ndeclare var jest: any;\ndeclare var jasmine: any;\ndeclare var describe: (name: string, callback: () => void) => void;\ndeclare var it: (name: string, callback: () => void) => void;\ndeclare var expect: any;\n\ndeclare module \"graphql\" {\n  declare var exports: any;\n}\n\ndeclare module \"react-native-linear-gradient\" {\n  declare var exports: any;\n}\n\ndeclare module \"resolveAssetSource\" {\n  declare var exports: any;\n}\n"
  },
  {
    "path": "js/login/LoginModal.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport { Dimensions, View, Image, StyleSheet } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport { Heading2 } from \"../common/F8Text\";\nimport F8BackgroundRepeat from \"../common/F8BackgroundRepeat\";\nimport F8Button from \"../common/F8Button\";\nimport LoginButton from \"../common/LoginButton\";\n\nimport F8Modal from \"../common/F8Modal\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  WINDOW_HEIGHT = Dimensions.get(\"window\").height,\n  RENDER_ARROW_SECTION = WINDOW_HEIGHT <= 600 ? false : true,\n  CONTENT_PADDING_V = WINDOW_HEIGHT <= 600 ? 20 : 32,\n  MODAL_PADDING_H = 10,\n  MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2;\n\n/* <LoginModal />\n============================================================================= */\n\nclass LoginModal extends React.Component {\n  props: {\n    navigator: Navigator,\n    onLogin: () => void\n  };\n\n  render() {\n    return (\n      <F8Modal\n        renderContent={this.renderContent}\n        renderFooter={this.renderFooter}\n        bottomGradient={[\n          F8Colors.colorWithAlpha(\"tangaroa\", 0),\n          F8Colors.colorWithAlpha(\"tangaroa\", 1)\n        ]}\n        {...this.props}\n      />\n    );\n  }\n\n  renderContent = _ => {\n    return (\n      <View>\n        <View style={styles.header}>\n          <F8BackgroundRepeat\n            width={MODAL_WIDTH}\n            height={210}\n            source={require(\"../common/img/pattern-dots.png\")}\n            style={styles.headerBackground}\n          />\n          <Image source={require(\"./img/login-modal.png\")} />\n        </View>\n        <View style={styles.content}>\n          <Heading2 style={styles.h2}>\n            {\"Log in to add sessions\\nto My F8.\"}\n          </Heading2>\n          {this.renderArrow()}\n          <LoginButton onLoggedIn={this.loggedIn} />\n        </View>\n      </View>\n    );\n  };\n\n  renderArrow() {\n    if (RENDER_ARROW_SECTION) {\n      return <Image style={styles.arrow} source={require(\"./img/arrow.png\")} />;\n    } else {\n      return null;\n    }\n  }\n\n  renderFooter = _ => {\n    return (\n      <F8Button\n        style={{ marginBottom: 27 }}\n        theme=\"white\"\n        type=\"round\"\n        icon={require(\"../common/img/buttons/icon-x.png\")}\n        onPress={this.dismiss}\n      />\n    );\n  };\n\n  dismiss = _ => {\n    this.props.onClose && this.props.onClose();\n  };\n\n  loggedIn = _ => {\n    this.props.onLogin();\n    this.dismiss();\n  };\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  header: {\n    alignItems: \"center\",\n    overflow: \"hidden\"\n  },\n  headerBackground: {\n    position: \"absolute\",\n    left: 0,\n    top: 0\n  },\n\n  content: {\n    paddingHorizontal: 23,\n    paddingVertical: CONTENT_PADDING_V,\n    alignItems: \"center\"\n  },\n  h2: {\n    color: F8Colors.blue,\n    textAlign: \"center\",\n    marginBottom: 20\n  },\n\n  arrow: {\n    marginBottom: 20\n  }\n});\n\n/* exports ================================================================== */\nmodule.exports = LoginModal;\n"
  },
  {
    "path": "js/login/LoginScreen.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\nimport { skipLogin } from \"../actions\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport { Text, Heading1 } from \"../common/F8Text\";\nimport {\n  Animated,\n  Dimensions,\n  Image,\n  StatusBar,\n  View,\n  TouchableOpacity,\n  StyleSheet\n} from \"react-native\";\nimport LoginButton from \"../common/LoginButton\";\n\n/* Config/Constants\n============================================================================= */\n\nconst SKIP_BTN_HEIGHT = 24,\n  WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  WINDOW_HEIGHT = Dimensions.get(\"window\").height,\n  VERTICAL_BREAKPOINT = WINDOW_HEIGHT <= 600,\n  HEADER_HEIGHT = VERTICAL_BREAKPOINT ? 220 : 285,\n  SKIP_BTN_MARGIN_TOP = VERTICAL_BREAKPOINT ? 15 : 23,\n  WHENWHERE_PADDING_TOP = VERTICAL_BREAKPOINT ? 12 : 18,\n  RENDER_ARROW_SECTION = VERTICAL_BREAKPOINT ? false : true,\n  LOGIN_PADDING_BOTTOM = VERTICAL_BREAKPOINT ? 20 : 33,\n  CONTENT_PADDING_H = VERTICAL_BREAKPOINT ? 15 : 20;\n\n/* =============================================================================\n<LoginScreen />\n--------------------------------------------------------------------------------\n\nProps:\n  ?\n\n============================================================================= */\n\nclass LoginScreen extends React.Component {\n  state = {\n    anim: new Animated.Value(0)\n  };\n\n  componentDidMount() {\n    Animated.timing(this.state.anim, { toValue: 3000, duration: 3000 }).start();\n  }\n\n  render() {\n    return (\n      <View style={styles.container}>\n        <StatusBar barStyle=\"default\" />\n        <View style={styles.header}>\n          <Image\n            source={require(\"../common/img/pattern-dots.png\")}\n            style={styles.headerPattern}\n          />\n          <Image\n            resizeMode=\"cover\"\n            source={require(\"./img/illustration.png\")}\n            style={styles.headerIllustration}\n          />\n          <Image source={require(\"./img/logo.png\")} />\n        </View>\n        <View style={styles.content}>\n          <View style={styles.mainHeadingSection}>\n            <Animated.View style={this.fadeIn(500, 5)}>\n              <Heading1 style={styles.h1}>\n                Facebook Developer Conference\n              </Heading1>\n            </Animated.View>\n            <Animated.Text\n              style={[styles.whenWhereText, this.fadeIn(1200, 10)]}\n            >\n              APRIL 18 + 19 / SAN JOSE, CALIFORNIA\n            </Animated.Text>\n          </View>\n\n          {this.renderArrowSection()}\n\n          <Animated.View style={[styles.loginSection, this.fadeIn(1900, 20)]}>\n            <Text style={styles.loginComment}>\n              Use Facebook to find your friends at F8.\n            </Text>\n            <LoginButton source=\"First screen\" />\n            <TouchableOpacity\n              onPress={_ => this.props.dispatch(skipLogin())}\n              style={styles.skipButton}\n            >\n              <Text style={styles.skipText}>SKIP FOR NOW</Text>\n            </TouchableOpacity>\n          </Animated.View>\n        </View>\n      </View>\n    );\n  }\n\n  renderArrowSection() {\n    if (RENDER_ARROW_SECTION) {\n      return (\n        <Animated.View style={[styles.arrowSection, this.fadeIn(1500, 15)]}>\n          <Image source={require(\"./img/arrow.png\")} />\n        </Animated.View>\n      );\n    } else {\n      return null;\n    }\n  }\n\n  fadeIn(delay, from = 0) {\n    const { anim } = this.state;\n    return {\n      opacity: anim.interpolate({\n        inputRange: [delay, Math.min(delay + 500, 3000)],\n        outputRange: [0, 1],\n        extrapolate: \"clamp\"\n      }),\n      transform: [\n        {\n          translateY: anim.interpolate({\n            inputRange: [delay, Math.min(delay + 500, 3000)],\n            outputRange: [from, 0],\n            extrapolate: \"clamp\"\n          })\n        }\n      ]\n    };\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.bianca\n  },\n\n  //header styles\n  header: {\n    height: HEADER_HEIGHT,\n    alignItems: \"center\",\n    justifyContent: \"flex-end\"\n  },\n  headerPattern: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    height: HEADER_HEIGHT - 30\n  },\n  headerIllustration: {\n    position: \"absolute\",\n    left: 0,\n    width: WINDOW_WIDTH,\n    bottom: 80\n  },\n\n  content: {\n    flex: 1,\n    justifyContent: \"space-around\",\n    paddingHorizontal: CONTENT_PADDING_H\n  },\n\n  h1: {\n    marginTop: 16,\n    textAlign: \"center\"\n  },\n  whenWhereText: {\n    marginTop: WHENWHERE_PADDING_TOP,\n    textAlign: \"center\",\n    color: F8Colors.tangaroa,\n    fontFamily: F8Fonts.helvetica\n  },\n\n  arrowSection: {\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n\n  loginSection: {\n    paddingBottom: LOGIN_PADDING_BOTTOM,\n    alignItems: \"center\",\n    paddingHorizontal: 20\n  },\n  loginComment: {\n    textAlign: \"center\",\n    fontSize: 15,\n    color: F8Colors.pink,\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\"),\n    marginBottom: 23\n  },\n  skipButton: {\n    marginTop: SKIP_BTN_MARGIN_TOP,\n    height: SKIP_BTN_HEIGHT,\n    alignSelf: \"stretch\",\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  skipText: {\n    color: F8Colors.colorWithAlpha(\"tangaroa\", 0.5),\n    fontFamily: F8Fonts.helvetica\n  }\n});\n\n/* Export\n============================================================================= */\nmodule.exports = connect()(LoginScreen);\n"
  },
  {
    "path": "js/rating/Header.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Image, Text, View, StyleSheet } from \"react-native\";\n\nimport type { Session } from \"../reducers/sessions\";\n\ntype Props = {\n  session: Session\n};\n\nfunction Header({ session }: Props) {\n  const pics = session.speakers.map(speaker => (\n    <Image key={speaker.id} source={{ uri: speaker.pic }} style={styles.pic} />\n  ));\n  return (\n    <View style={styles.container}>\n      <Text style={styles.title}>{session.title}</Text>\n      <View style={styles.speakers}>{pics}</View>\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    height: 170,\n    paddingHorizontal: 10\n  },\n  background: {\n    alignItems: \"center\",\n    position: \"absolute\",\n    bottom: 0,\n    left: 0,\n    right: 0\n  },\n  label: {\n    fontSize: 12,\n    color: F8Colors.lightText,\n    letterSpacing: 1\n  },\n  title: {\n    marginTop: 10,\n    fontSize: 17,\n    fontWeight: \"bold\",\n    color: F8Colors.darkText,\n    textAlign: \"center\"\n  },\n  speakers: {\n    marginTop: 15,\n    flexDirection: \"row\"\n  },\n  pic: {\n    width: 40,\n    height: 40,\n    borderRadius: 20,\n    margin: 2\n  }\n});\n\nmodule.exports = Header;\nmodule.exports.__cards__ = define => {\n  const MOCK_SESSION = {\n    id: \"mock1\",\n    title: \"Building For the Next Billion\",\n    speakers: [\n      {\n        id: \"1\",\n        bio: \"\",\n        name: \"Foo\",\n        title: \"\",\n        pic:\n          \"https://graph.facebook.com/100001244322535/picture?width=60&height=60\"\n      },\n      {\n        id: \"2\",\n        bio: \"\",\n        name: \"Bar\",\n        title: \"\",\n        pic:\n          \"https://graph.facebook.com/10152531777042364/picture?width=60&height=60\"\n      }\n    ],\n    day: 1,\n    allDay: false,\n    description: \"...\",\n    startTime: 0,\n    endTime: 0,\n    hasDetails: true,\n    location: \"space\",\n    map: \"space\",\n    onMySchedule: false,\n    slug: \"next-billion\",\n    tags: []\n  };\n\n  define(\"Example\", (state = null, update) => (\n    <Header session={MOCK_SESSION} />\n  ));\n\n  define(\"Long title\", () => (\n    <Header\n      session={{\n        ...MOCK_SESSION,\n        title:\n          \"Inside Facebook's Infrastructure (Part 1): The System that Serves Billions\",\n        speakers: []\n      }}\n    />\n  ));\n};\n"
  },
  {
    "path": "js/rating/RadioButtons.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport { View, TouchableOpacity, Image } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport { Text } from \"../common/F8Text\";\n\n/* <RadioButtons /> ========================================================= */\n\nclass RadioButtons extends React.Component {\n  static defaultProps = {\n    options: []\n  };\n\n  render() {\n    return (\n      <View style={this.props.style}>\n        {this.props.options.map((text, idx) => this.renderAnswer(text, idx))}\n      </View>\n    );\n  }\n\n  onPress = (value, idx) => {\n    this.props.onChange && this.props.onChange(idx);\n  };\n\n  renderAnswer(text, idx) {\n    const isActive = this.props.selectedIndex === idx;\n    const source = isActive\n      ? require(\"./img/radio-active.png\")\n      : require(\"./img/radio-default.png\");\n    const accessibilityTraits = [\"button\"];\n    if (isActive) {\n      accessibilityTraits.push(\"selected\");\n    }\n\n    return (\n      <TouchableOpacity\n        key={`${idx}${text}`}\n        accessibilityLabel={text}\n        accessibilityTraits={accessibilityTraits}\n        activeOpacity={0.8}\n        style={styles.container}\n        onPress={_ => this.onPress(text, idx)}\n      >\n        <Image style={styles.icon} source={source} />\n        <Text style={styles.text}>{text}</Text>\n      </TouchableOpacity>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    paddingVertical: 10,\n    flexDirection: \"row\",\n    alignItems: \"center\"\n  },\n  text: {\n    paddingLeft: 16,\n    fontSize: F8Fonts.normalize(17),\n    color: F8Colors.tangaroa,\n    flex: 1\n  }\n});\n\n/* exports & Playground cards =============================================== */\n\nmodule.exports = RadioButtons;\nmodule.exports.__cards = define => {\n  const MOCK_RADIO_BUTTONS = [\"First option\", \"Second option\", \"Third option\"];\n\n  define(\"Default\", (state = null, update) => (\n    <RadioButtons\n      options={MOCK_RADIO_BUTTONS}\n      selectedIndex={state}\n      onChange={update}\n    />\n  ));\n\n  define(\"Selected: First\", (state = 0, update) => (\n    <RadioButtons\n      options={MOCK_RADIO_BUTTONS}\n      selectedIndex={state}\n      onChange={update}\n    />\n  ));\n};\n"
  },
  {
    "path": "js/rating/RatingQuestion.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport { View } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport { Paragraph } from \"../common/F8Text\";\n\nimport StarRating from \"./StarRating\";\nimport RadioButtons from \"./RadioButtons\";\nimport WrittenResponse from \"./WrittenResponse\";\nimport StarRatings from \"./StarRatings\";\n\n/* <RatingQuestion />\n============================================================================= */\n\nexport default class RatingQuestion extends React.Component {\n  render() {\n    const type = this.getQuestionType();\n\n    return (\n      <View style={this.props.style}>\n        {this.renderQuestionText(type)}\n        {this.renderQuestionByType(type)}\n      </View>\n    );\n  }\n\n  getQuestionType() {\n    const { question } = this.props;\n    if (question.written) {\n      return \"written\";\n    } else if (question.feedback) {\n      return \"feedback\";\n    } else if (question.radios) {\n      return \"radios\";\n    } else if (question.ratings) {\n      return \"ratings\";\n    } else if (question.rating) {\n      return \"rating\";\n    } else {\n      return null;\n    }\n  }\n\n  renderQuestionByType(type) {\n    const { question, rating, onChange } = this.props;\n\n    if (type === \"written\") {\n      const { placeholder, maxLength } = question.written;\n      return (\n        <WrittenResponse\n          placeholder={placeholder}\n          maxLength={maxLength}\n          onChange={onChange}\n        />\n      );\n    } else if (type === \"feedback\") {\n      return (\n        <WrittenResponse placeholder={question.text} onChange={onChange} />\n      );\n    } else if (type === \"radios\") {\n      return (\n        <RadioButtons\n          options={question.radios}\n          selectedIndex={rating}\n          onChange={onChange}\n        />\n      );\n    } else if (type === \"rating\") {\n      return (\n        <View style={{ marginTop: 15 }}>\n          <StarRating\n            labels={question.rating}\n            rating={rating}\n            onChange={onChange}\n          />\n        </View>\n      );\n    } else if (type === \"ratings\") {\n      const { rows, labels } = question.ratings;\n      return (\n        <StarRatings\n          rows={rows}\n          labels={labels}\n          rating={rating}\n          onChange={onChange}\n        />\n      );\n    } else {\n      return null;\n    }\n  }\n\n  renderQuestionText(type) {\n    if (type === \"feedback\") {\n      return null;\n    } else {\n      return (\n        <Paragraph style={styles.questionText}>\n          {this.props.question.text}\n        </Paragraph>\n      );\n    }\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  questionText: {\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\"),\n    color: F8Colors.tangaroa,\n    fontSize: F8Fonts.normalize(17),\n    textAlign: \"center\",\n    marginBottom: 10\n  }\n});\n"
  },
  {
    "path": "js/rating/RatingScreen.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { View, ToastAndroid, Platform } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport F8Header from \"../common/F8Header\";\nimport { connect } from \"react-redux\";\nimport { submitSurveyAnswers } from \"../actions\";\n\nimport type { Survey } from \"../reducers/surveys\";\nimport type { Session } from \"../reducers/sessions\";\nimport type { Dispatch } from \"../actions/types\";\n\nimport RatingQuestion from \"./RatingQuestion\";\nimport { Heading2, Paragraph } from \"../common/F8Text\";\nimport { KeyboardAwareScrollView } from \"react-native-keyboard-aware-scroll-view\";\n\nimport F8Colors from \"../common/F8Colors\";\nimport F8Button from \"../common/F8Button\";\nimport F8Toast from \"../common/F8Toast\";\n\ntype Props = {\n  sessions: Array<Session>,\n  surveys: Array<Survey>,\n  navigator: any,\n  dispatch: Dispatch\n};\n\nclass RatingScreen extends React.Component {\n  props: Props;\n  state: {\n    selectedIndex: number\n  };\n\n  static defaultProps = {\n    type: \"Session\",\n    successMessage: \"Rating sent!\"\n  };\n\n  constructor(props: Props) {\n    super(props);\n\n    let defaultValues = {};\n    (props.survey.questions || []).map((q, idx) => {\n      if (q.optional) {\n        defaultValues[idx] = \"\";\n      }\n    });\n    this.state = { ...defaultValues };\n  }\n\n  render() {\n    let rightItem;\n    if (Platform.OS === \"ios\" && this.isValid()) {\n      rightItem = {\n        title: \"Submit\",\n        icon: require(\"../common/img/header/confirm.png\"),\n        onPress: this.submit\n      };\n    }\n\n    return (\n      <View style={styles.container}>\n        <F8Header\n          backgroundColor={F8Colors.salmon}\n          title={`Review ${this.props.type}`}\n          leftItem={{\n            title: \"Cancel\",\n            icon: require(\"../common/img/header/x.png\"),\n            onPress: _ => this.props.navigator.pop()\n          }}\n          rightItem={rightItem}\n        />\n        {this.renderForm()}\n      </View>\n    );\n  }\n\n  renderForm() {\n    const { sessions, survey, successMessage } = this.props;\n    const session = sessions.find(s => s.id === survey.sessionId);\n\n    const questions = survey.questions.map((question, ii) => (\n      <RatingQuestion\n        key={ii}\n        style={styles.question}\n        question={question}\n        rating={this.state[ii]}\n        onChange={rating => this.setState({ [ii]: rating })}\n      />\n    ));\n\n    return (\n      <KeyboardAwareScrollView showsVerticalScrollIndicator={false}>\n        {this.renderHeader(session.title, survey.description)}\n        {questions}\n        {Platform.OS === \"android\" ? this.renderSubmitButton() : null}\n        {this.state.iosSuccessMessage ? (\n          <F8Toast onComplete={this.dismiss} text={successMessage} />\n        ) : null}\n      </KeyboardAwareScrollView>\n    );\n  }\n\n  renderHeader(title, description) {\n    return (\n      <View style={styles.header}>\n        {title ? <Heading2 style={styles.heading}>{title}</Heading2> : null}\n        {description ? (\n          <Paragraph style={styles.description}>{description}</Paragraph>\n        ) : null}\n      </View>\n    );\n  }\n\n  renderSubmitButton() {\n    let btn;\n    if (this.isValid()) {\n      btn = <F8Button caption=\"Submit Rating\" onPress={this.submit} />;\n    } else {\n      btn = <F8Button theme=\"disabled\" caption=\"Submit Rating\" />;\n    }\n\n    return <View style={styles.footer}>{btn}</View>;\n  }\n\n  submit = () => {\n    const { survey } = this.props;\n    const answers = survey.questions.map((_, ii) => this.state[ii]);\n    this.props.dispatch(submitSurveyAnswers(survey.id, answers)).then(() => {\n      if (Platform.OS === \"ios\") {\n        this.setState({ iosSuccessMessage: true });\n      } else {\n        this.dismiss();\n      }\n    });\n  };\n\n  dismiss = _ => {\n    this.props.navigator.pop();\n    if (Platform.OS === \"android\") {\n      ToastAndroid.show(this.props.successMessage, ToastAndroid.SHORT);\n    }\n  };\n\n  isValid() {\n    const { questions } = this.props.survey;\n    return Object.keys(this.state).length === questions.length;\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.white\n  },\n\n  header: {\n    alignItems: \"center\",\n    paddingVertical: 30,\n    paddingHorizontal: 22\n  },\n  // subheading:{\n  //   fontFamily: F8Fonts.helvetica,\n  //   fontSize: 15,\n  //   color: F8Colors.blue,\n  //   marginBottom: 15\n  // },\n  heading: {\n    textAlign: \"center\",\n    color: F8Colors.blue\n    // marginBottom: 15,\n  },\n\n  description: {\n    textAlign: \"center\",\n    marginTop: 12\n  },\n\n  question: {\n    paddingHorizontal: 22, // was:30\n    marginBottom: 30\n  },\n\n  footer: {\n    paddingHorizontal: 22,\n    paddingBottom: 30\n  }\n});\n\nfunction select(store) {\n  return {\n    sessions: store.sessions\n  };\n}\n\nmodule.exports = connect(select)(RatingScreen);\n"
  },
  {
    "path": "js/rating/StarRating.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { View, TouchableOpacity, Image } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport { Text } from \"../common/F8Text\";\n\n/* <StarRating />\n============================================================================= */\n\nfunction StarRating({ labels, rating, onChange }) {\n  const stars = [1, 2, 3, 4, 5].map(value => (\n    <Star\n      key={value}\n      value={value}\n      isFull={rating && value <= rating}\n      onPress={_ => onChange(value)}\n    />\n  ));\n\n  let labelSection;\n  if (labels && labels.low && labels.high) {\n    labelSection = (\n      <View style={styles.labels}>\n        <Text style={styles.label}>{labels.low}</Text>\n        <Text style={styles.label}>{labels.high}</Text>\n      </View>\n    );\n  }\n\n  return (\n    <View style={styles.container}>\n      <View style={styles.stars}>{stars}</View>\n      {labelSection}\n    </View>\n  );\n}\n\n/* <Star />\n============================================================================= */\n\nfunction Star({ isFull, value, onPress }) {\n  const source = isFull\n    ? require(\"./img/star-active.png\")\n    : require(\"./img/star-default.png\");\n\n  const accessibilityTraits = [\"button\"];\n  if (isFull) {\n    accessibilityTraits.push(\"selected\");\n  }\n\n  return (\n    <TouchableOpacity\n      accessibilityLabel={`${value} stars`}\n      accessibilityTraits={accessibilityTraits}\n      style={styles.star}\n      activeOpacity={0.8}\n      onPress={onPress}\n    >\n      <Image source={source} />\n    </TouchableOpacity>\n  );\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    alignSelf: \"center\",\n    width: 298\n  },\n  stars: {\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n    width: 260,\n    // backgroundColor: 'yellow',\n    alignSelf: \"center\"\n  },\n  star: {\n    flex: 1,\n    alignItems: \"center\"\n  },\n  labels: {\n    // paddingHorizontal: 14,\n    paddingTop: 20,\n    flexDirection: \"row\",\n    justifyContent: \"space-between\"\n  },\n  label: {\n    fontSize: 11,\n    width: 90,\n    color: \"rgba(3,34,80,1)\",\n    textAlign: \"center\"\n    // backgroundColor: 'red'\n  }\n});\n\n/* exports & Playground cards =============================================== */\n\nmodule.exports = StarRating;\nmodule.exports.__cards = define => {\n  define(\"Default\", (state = null, update) => (\n    <StarRating\n      labels={{ low: \"Not at all\", high: \"Very likely\" }}\n      rating={state}\n      onChange={update}\n    />\n  ));\n};\n"
  },
  {
    "path": "js/rating/StarRatings.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport { View } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\nimport { Text } from \"../common/F8Text\";\nimport StarRating from \"./StarRating\";\n\n/* StarRatings =============================================================== */\n\nclass StarRatings extends React.Component {\n  static defaultProps = {\n    rows: []\n  };\n\n  constructor(props) {\n    super(props);\n    this.state = {};\n  }\n\n  componentWillUpdate(nextProps, nextState) {\n    if (nextState !== this.state) {\n      const { rows } = this.props;\n      if (Object.keys(nextState).length === rows.length) {\n        const answers = rows.map((_, ii) => nextState[ii]);\n        this.props.onChange && this.props.onChange(answers);\n      }\n    }\n  }\n\n  render() {\n    const { rows, labels } = this.props;\n    if (!rows.length) {\n      return null;\n    }\n\n    const content = rows.map((row, idx) => (\n      <View key={`${row}${idx}`} style={styles.row}>\n        <Text style={styles.heading}>{row.toUpperCase()}</Text>\n        <StarRating\n          labels={labels}\n          rating={this.state[idx]}\n          onChange={val => this.setState({ [idx]: val })}\n        />\n      </View>\n    ));\n\n    return <View style={styles.container}>{content}</View>;\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  row: {\n    marginVertical: 15\n  },\n  heading: {\n    fontFamily: F8Fonts.helvetica,\n    fontSize: F8Fonts.normalize(15),\n    textAlign: \"center\",\n    color: F8Colors.blue,\n    marginBottom: 17\n  }\n});\n\n/* exports & Playground cards =============================================== */\n\nmodule.exports = StarRatings;\nmodule.exports.__cards = define => {\n  define(\"Default\", (state = null, update) => (\n    <StarRatings\n      rows={[]}\n      labels={{ low: \"Not at all\", high: \"Very likely\" }}\n      rating={state}\n      onChange={update}\n    />\n  ));\n};\n"
  },
  {
    "path": "js/rating/WrittenResponse.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Fonts from \"../common/F8Fonts\";\nimport { Platform, View, TextInput, Alert } from \"react-native\";\nimport StyleSheet from \"../common/F8StyleSheet\";\n\n/* <WrittenResponse />\n============================================================================= */\n\nclass WrittenResponse extends React.Component {\n  static defaultProps = {\n    multiline: true,\n    maxLength: 500,\n    inputColor: F8Colors.coolGray,\n    textColor: F8Colors.tangaroa,\n    onChange: _ => {}\n  };\n\n  render() {\n    const {\n      onChange,\n      multiline,\n      maxLength,\n      placeholder,\n      inputColor,\n      textColor\n    } = this.props;\n\n    let platformStyles;\n    if (Platform.OS === \"ios\") {\n      platformStyles = { borderColor: inputColor, color: textColor };\n    } else {\n      platformStyles = { color: textColor };\n    }\n\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <TextInput\n          style={[styles.input, platformStyles]}\n          multiline={multiline}\n          maxLength={maxLength}\n          placeholder={placeholder}\n          placeholderTextColor={inputColor}\n          underlineColorAndroid={inputColor}\n          onEndEditing={event => onChange(event.nativeEvent.text)}\n        />\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  wrapper: {\n    flex: 1,\n    justifyContent: \"center\",\n    paddingVertical: 15\n  },\n  input: {\n    fontFamily: F8Fonts.default,\n    fontSize: F8Fonts.normalize(17),\n    color: F8Colors.tangaroa,\n    height: 100,\n\n    ios: {\n      borderRadius: 5,\n      borderWidth: 1,\n      borderColor: F8Colors.coolGray,\n      paddingVertical: 15,\n      paddingHorizontal: 20\n    }\n  }\n});\n\n/* exports & Playground cards =============================================== */\n\nmodule.exports = WrittenResponse;\nmodule.exports.__cards = define => {\n  define(\"Written\", _ => (\n    <WrittenResponse onChange={value => Alert.alert(value)} />\n  ));\n};\n"
  },
  {
    "path": "js/reducers/__mocks__/parse.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nclass ParseObjectMock {\n  id: string;\n  createdAt: Date;\n  _fields: Object;\n\n  constructor(fields: Object) {\n    this._fields = fields;\n    this.id = Math.ceil(Math.random() * 0xffffff).toString(16);\n    this.createdAt = new Date();\n  }\n\n  get(name: string): any {\n    return this._fields[name];\n  }\n}\n\nclass ParseFileMock {\n  _url: string;\n\n  constructor(url: string) {\n    this._url = url;\n  }\n\n  url(): string {\n    return this._url;\n  }\n}\n\nmodule.exports = {\n  Object: ParseObjectMock,\n  File: ParseFileMock\n};\n"
  },
  {
    "path": "js/reducers/__tests__/maps-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\njest.autoMockOff();\n\nimport Parse from \"parse\";\nimport maps from \"../maps\";\n\ndescribe(\"maps reducer\", () => {\n  it(\"is empty by default\", () => {\n    expect(maps(undefined, {})).toEqual([]);\n  });\n\n  it(\"populates maps from server\", () => {\n    let list = [\n      new Parse.Object({\n        name: \"Day 1\",\n        x1: new Parse.File(\"x1.png\"),\n        x2: new Parse.File(\"x2.png\"),\n        x3: new Parse.File(\"x3.png\")\n      })\n    ];\n\n    expect(maps([], { type: \"LOADED_MAPS\", list })).toEqual([\n      {\n        id: jasmine.any(String),\n        name: \"Day 1\",\n        x1url: \"x1.png\",\n        x2url: \"x2.png\",\n        x3url: \"x3.png\"\n      }\n    ]);\n  });\n});\n"
  },
  {
    "path": "js/reducers/__tests__/notifications-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport Parse from \"parse\";\n\njest.dontMock(\"../notifications\");\njest.dontMock(\"crc32\");\nimport notifications from \"../notifications\";\n\nconst emptyAction: any = {};\nconst empty = {\n  server: [],\n  push: [],\n  enabled: null,\n  registered: false,\n  seen: {}\n};\n\ndescribe(\"notifications reducer\", () => {\n  it(\"is empty by default\", () => {\n    expect(notifications(undefined, emptyAction)).toEqual(empty);\n  });\n\n  it(\"populates notifications from server\", () => {\n    let list = [\n      new Parse.Object({ text: \"hello\", url: \"https://fbf8.com\" }),\n      new Parse.Object({ text: \"bye\", url: null })\n    ];\n\n    let { server } = notifications(empty, {\n      type: \"LOADED_NOTIFICATIONS\",\n      list\n    });\n\n    expect(server).toEqual([\n      {\n        id: jasmine.any(String),\n        text: \"hello\",\n        url: \"https://fbf8.com\",\n        time: jasmine.any(Number)\n      },\n      {\n        id: jasmine.any(String),\n        text: \"bye\",\n        url: null,\n        time: jasmine.any(Number)\n      }\n    ]);\n  });\n\n  it(\"skips duplicates\", () => {\n    const notification = {\n      text: \"Hello, world!\",\n      url: null,\n      time: 1234567\n    };\n\n    const action1 = {\n      type: \"RECEIVED_PUSH_NOTIFICATION\",\n      notification: { ...notification }\n    };\n    const action2 = {\n      type: \"RECEIVED_PUSH_NOTIFICATION\",\n      notification: { ...notification }\n    };\n\n    const { push } = notifications(notifications(empty, action1), action2);\n    expect(push).toEqual([\n      {\n        id: jasmine.any(String),\n        ...notification\n      }\n    ]);\n  });\n});\n"
  },
  {
    "path": "js/reducers/__tests__/schedule-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\njest.dontMock(\"../schedule\");\nimport schedule from \"../schedule\";\n\ndescribe(\"schedule reducer\", () => {\n  it(\"is empty by default\", () => {\n    expect(schedule(undefined, ({}: any))).toEqual({});\n  });\n\n  it(\"adds sessions to schedule\", () => {\n    expect(schedule({}, { type: \"SESSION_ADDED\", id: \"one\" })).toEqual({\n      one: true\n    });\n\n    expect(\n      schedule({ one: true }, { type: \"SESSION_ADDED\", id: \"two\" })\n    ).toEqual({ one: true, two: true });\n  });\n\n  it(\"removes sessions from schedule\", () => {\n    expect(\n      schedule(\n        {\n          one: true,\n          two: true\n        },\n        {\n          type: \"SESSION_REMOVED\",\n          id: \"two\"\n        }\n      )\n    ).toEqual({\n      one: true\n    });\n  });\n\n  it(\"restores schedule when logging in\", () => {\n    expect(\n      schedule(\n        {\n          one: true\n        },\n        {\n          type: \"RESTORED_SCHEDULE\",\n          list: [{ id: \"two\" }, { id: \"three\" }]\n        }\n      )\n    ).toEqual({\n      two: true,\n      three: true\n    });\n  });\n\n  it(\"clears schedule when logging out\", () => {\n    expect(\n      schedule(\n        {\n          one: true,\n          two: true\n        },\n        {\n          type: \"LOGGED_OUT\"\n        }\n      )\n    ).toEqual({});\n  });\n});\n"
  },
  {
    "path": "js/reducers/config.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type Config = {\n  wifiNetwork: string,\n  wifiPassword: string,\n  appLinkURL: string,\n  appInvitePreviewImageURL: string,\n  sessionURLTemplate: string,\n  thirdPartyNotices: string,\n  manageBookingsURL: string\n};\n\nconst initialState: Config = {\n  wifiNetwork: \"\",\n  wifiPassword: \"\",\n  appLinkURL: \"https://www.fbf8.com/\",\n  appInvitePreviewImageURL: \"\",\n  sessionURLTemplate: \"https://www.fbf8.com/schedule/session/{slug}\",\n  thirdPartyNotices: \"\",\n  manageBookingsURL: \"https://demos.fbf8.com/manage-reservations\"\n};\n\nfunction config(state: Config = initialState, action: Action): Config {\n  if (action.type === \"LOADED_CONFIG\") {\n    return {\n      wifiNetwork: action.config.get(\"wifiNetwork\") || state.wifiNetwork,\n      wifiPassword: action.config.get(\"wifiPassword\") || state.wifiPassword,\n      appLinkURL: action.config.get(\"appLinkURL\") || state.appLinkURL,\n      appInvitePreviewImageURL:\n        action.config.get(\"appInvitePreviewImageURL\") ||\n        state.appInvitePreviewImageURL,\n      sessionURLTemplate:\n        action.config.get(\"sessionURLTemplate\") || state.sessionURLTemplate,\n      manageBookingsURL:\n        action.config.get(\"manageBookingsURL\") || state.manageBookingsURL\n    };\n  }\n\n  return state;\n}\n\nmodule.exports = config;\n"
  },
  {
    "path": "js/reducers/createParseReducer.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\ntype Convert<T> = (object: Object) => T;\ntype Reducer<T> = (state: ?Array<T>, action: any) => Array<T>;\n\nfunction createParseReducer<T>(type: string, convert: Convert<T>): Reducer<T> {\n  return function(state: ?Array<T>, action: Action): Array<T> {\n    if (action.type === type) {\n      // Flow can't guarantee {type, list} is a valid action\n      return (action: any).list.map(convert);\n    }\n    return state || [];\n  };\n}\n\nmodule.exports = createParseReducer;\n"
  },
  {
    "path": "js/reducers/faqs.js",
    "content": "/**\n * Copyright 2014 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport createParseReducer from \"./createParseReducer\";\n\nexport type FAQ = {\n  id: string,\n  question: string,\n  answer: string\n};\n\nfunction fromParseObject(map: Object): FAQ {\n  return {\n    id: map.id,\n    question: map.get(\"question\"),\n    answer: map.get(\"answer\")\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_FAQS\", fromParseObject);\n"
  },
  {
    "path": "js/reducers/friendsSchedules.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type FriendsSchedule = {\n  id: string,\n  name: string,\n  schedule: { [key: string]: boolean }\n};\n\ntype State = Array<FriendsSchedule>;\n\nfunction friendsSchedules(state: State = [], action: Action): State {\n  if (action.type === \"LOADED_FRIENDS_SCHEDULES\") {\n    return action.list;\n  }\n  if (action.type === \"LOGGED_OUT\") {\n    return [];\n  }\n  return state;\n}\n\nmodule.exports = friendsSchedules;\n"
  },
  {
    "path": "js/reducers/index.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { combineReducers } from \"redux\";\n\nmodule.exports = combineReducers({\n  config: require(\"./config\"),\n  notifications: require(\"./notifications\"),\n  maps: require(\"./maps\"),\n  sessions: require(\"./sessions\"),\n  user: require(\"./user\"),\n  schedule: require(\"./schedule\"),\n  scheduleTopics: require(\"./scheduleTopics\"),\n  scheduleFilter: require(\"./scheduleFilter\"),\n  faqs: require(\"./faqs\"),\n  pages: require(\"./pages\"),\n  navigation: require(\"./navigation\"),\n  friendsSchedules: require(\"./friendsSchedules\"),\n  surveys: require(\"./surveys\"),\n  videos: require(\"./videos\"),\n  videoTopics: require(\"./videoTopics\"),\n  videoFilter: require(\"./videoFilter\"),\n  policies: require(\"./policies\"),\n\n  testEventDates: require(\"./testEventDates\")\n});\n"
  },
  {
    "path": "js/reducers/maps.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport createParseReducer from \"./createParseReducer\";\n\nexport type Map = {\n  id: string,\n  name: string,\n  x1url: string,\n  x2url: string,\n  x3url: string,\n  width: number,\n  height: number\n};\n\nfunction fromParseObject(map: Object): Map {\n  return {\n    id: map.id,\n    name: map.get(\"name\"),\n    x1url: map.get(\"x1\") && map.get(\"x1\").url(),\n    x2url: map.get(\"x2\") && map.get(\"x2\").url(),\n    x3url: map.get(\"x3\") && map.get(\"x3\").url(),\n    width: map.get(\"width\"),\n    height: map.get(\"height\")\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_MAPS\", fromParseObject);\n"
  },
  {
    "path": "js/reducers/navigation.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type Tab = \"schedule\" | \"my-schedule\" | \"map\" | \"notifications\" | \"info\";\n\nexport type Day = 1 | 2;\n\ntype State = {\n  tab: Tab,\n  day: Day\n};\n\nconst initialState: State = { tab: \"schedule\", day: 1 };\n\nfunction navigation(state: State = initialState, action: Action): State {\n  if (action.type === \"SWITCH_TAB\") {\n    return { ...state, tab: action.tab };\n  }\n  if (action.type === \"SWITCH_DAY\") {\n    return { ...state, day: action.day };\n  }\n  if (action.type === \"LOGGED_OUT\") {\n    return initialState;\n  }\n  return state;\n}\n\nmodule.exports = navigation;\n"
  },
  {
    "path": "js/reducers/notifications.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { Platform } from \"react-native\";\nimport crc32 from \"crc32\";\n\nexport type Notification = {\n  id: string,\n  url: ?string,\n  urlTitle: ?string,\n  text: string,\n  time: number,\n  image: ?string\n};\n\nexport type SeenNotifications = {\n  [id: string]: boolean\n};\n\ntype State = {\n  enabled: ?boolean, // null = no answer\n  registered: boolean,\n\n  // Most notifications will be stored on Parse Core, so that\n  // people who installed the app after the conference started can\n  // get access. But some notifications will be delivered\n  // via push and only to subset of attendees.\n  server: Array<Notification>,\n  push: Array<Notification>,\n\n  seen: SeenNotifications\n};\n\nconst initialState = {\n  server: [],\n  push: [],\n  enabled: Platform.OS === \"ios\" ? null : true,\n  registered: false,\n  seen: {}\n};\n\nimport type { Action } from \"../actions/types\";\n\nfunction notifications(state: State = initialState, action: Action): State {\n  switch (action.type) {\n    case \"LOADED_NOTIFICATIONS\":\n      let list = action.list.map(fromParseObject);\n      return { ...state, server: list };\n\n    case \"RECEIVED_PUSH_NOTIFICATION\":\n      return { ...state, push: append(action.notification, state.push) };\n\n    case \"LOGGED_OUT\":\n      return { ...state, push: [] };\n\n    case \"TURNED_ON_PUSH_NOTIFICATIONS\":\n      return { ...state, enabled: true };\n\n    case \"SKIPPED_PUSH_NOTIFICATIONS\":\n      return { ...state, enabled: false };\n\n    case \"REGISTERED_PUSH_NOTIFICATIONS\":\n      return { ...state, registered: true };\n\n    case \"RESET_NUXES\":\n      return { ...state, enabled: initialState.enabled };\n\n    case \"SEEN_ALL_NOTIFICATIONS\":\n      return { ...state, seen: fetchAllIds([...state.server, ...state.push]) };\n\n    default:\n      return state;\n  }\n}\n\nfunction append(notification, list) {\n  const id =\n    notification.id || crc32(notification.text + notification.url).toString(36);\n  if (list.find(n => n.id === id)) {\n    return list;\n  }\n  return [{ id, ...notification }, ...list];\n}\n\nfunction fetchAllIds(notifs: Array<Notification>): SeenNotifications {\n  const seen = {};\n  notifs.forEach(notification => {\n    seen[notification.id] = true;\n  });\n  return seen;\n}\n\nfunction fromParseObject(object: Object): Notification {\n  return {\n    id: object.id,\n    text: object.get(\"text\"),\n    url: object.get(\"url\"),\n    urlTitle: object.get(\"urlTitle\"),\n    time: object.createdAt.getTime(),\n    image: object.get(\"image\")\n  };\n}\n\nmodule.exports = notifications;\n"
  },
  {
    "path": "js/reducers/pages.js",
    "content": "/**\n * Copyright 2014 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport createParseReducer from \"./createParseReducer\";\n\nexport type Page = {\n  id: string,\n  title: string,\n  url: string,\n  logo: ?string\n};\n\nfunction fromParseObject(map: Object): Page {\n  return {\n    id: map.id,\n    title: map.get(\"title\"),\n    url: map.get(\"url\"),\n    logo: map.get(\"logo\") && map.get(\"logo\").url()\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_PAGES\", fromParseObject);\n"
  },
  {
    "path": "js/reducers/policies.js",
    "content": "/**\n * Copyright 2014 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport createParseReducer from \"./createParseReducer\";\n\nexport type Policy = {\n  id: string,\n  title: string,\n  url: string\n};\n\nfunction fromParseObject(policy: Object): Policy {\n  return {\n    id: policy.id,\n    title: policy.get(\"title\"),\n    url: policy.get(\"url\")\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_POLICIES\", fromParseObject);\n"
  },
  {
    "path": "js/reducers/schedule.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type State = {\n  [id: string]: boolean\n};\n\nfunction schedule(state: State = {}, action: Action): State {\n  switch (action.type) {\n    case \"SESSION_ADDED\":\n      let added = {};\n      added[action.id] = true;\n      return { ...state, ...added };\n\n    case \"SESSION_REMOVED\":\n      let rest = { ...state };\n      delete rest[action.id];\n      return rest;\n\n    case \"LOGGED_OUT\":\n      return {};\n\n    case \"RESTORED_SCHEDULE\":\n      let all = {};\n      action.list.forEach(session => {\n        all[session.id] = true;\n      });\n      return all;\n  }\n  return state;\n}\n\nmodule.exports = schedule;\n"
  },
  {
    "path": "js/reducers/scheduleFilter.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type FriendFilter = {\n  id: string,\n  name: string,\n  schedule: { [key: string]: boolean }\n};\n\nexport type TopicsFilter = {\n  [key: string]: boolean\n};\n\ntype State = TopicsFilter;\n\nfunction filter(state: State = {}, action: Action): State {\n  if (action.type === \"APPLY_SCHEDULE_TOPICS_FILTER\") {\n    return action.scheduleTopics;\n  }\n  if (action.type === \"CLEAR_SCHEDULE_FILTER\") {\n    return {};\n  }\n  return state;\n}\n\nmodule.exports = filter;\n"
  },
  {
    "path": "js/reducers/scheduleTopics.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\ntype State = Array<string>;\ntype Action = { type: string, list: Array<any> };\n\nfunction topics(state: State = [], action: Action): State {\n  if (action.type === \"LOADED_SESSIONS\") {\n    const topicsMap = Object.create(null);\n    action.list.forEach(session => {\n      const tags = session.get(\"tags\") || [];\n      tags.forEach(tag => {\n        topicsMap[tag] = true;\n      });\n    });\n    return Object.keys(topicsMap).sort();\n  }\n  return state;\n}\n\nmodule.exports = topics;\n"
  },
  {
    "path": "js/reducers/sessions.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport { parseTimeToUTC } from \"../common/convertTimes\";\nimport createParseReducer from \"./createParseReducer\";\n\nexport type Speaker = {\n  id: string,\n  bio: string,\n  name: string,\n  pic: string,\n  title: string\n};\n\nexport type Session = {\n  id: string,\n  day: number,\n  allDay: boolean,\n  title: string,\n  description: string,\n  hasDetails: boolean,\n  slug: string,\n  speakers: Array<Speaker>,\n  onMySchedule: boolean,\n  tags: Array<string>,\n  startTime: number,\n  endTime: number,\n  map: ?string,\n  location: ?string\n};\n\nfunction fromParseSpeaker(speaker: Object): Speaker {\n  const pic = speaker.get(\"speakerPic\");\n  return {\n    id: speaker.id,\n    bio: speaker.get(\"speakerBio\"),\n    name: speaker.get(\"speakerName\"),\n    pic: pic && pic.url(),\n    title: speaker.get(\"speakerTitle\")\n  };\n}\n\nfunction fromParseSessions(session: Object): Session {\n  return {\n    id: session.id,\n    day: session.get(\"day\"),\n    allDay: session.get(\"allDay\"),\n    title: session.get(\"sessionTitle\"),\n    description: session.get(\"sessionDescription\"),\n    hasDetails: session.get(\"hasDetails\"),\n    slug: session.get(\"sessionSlug\"),\n    speakers: (session.get(\"speakers\") || []).map(fromParseSpeaker),\n    onMySchedule: session.get(\"onMySchedule\"),\n    tags: session.get(\"tags\") || [],\n    startTime:\n      session.get(\"startTime\") &&\n      session.get(\"startTime\").getTime() &&\n      parseTimeToUTC(session.get(\"startTime\").getTime()),\n    endTime:\n      session.get(\"endTime\") &&\n      session.get(\"endTime\").getTime() &&\n      parseTimeToUTC(session.get(\"endTime\").getTime()),\n    map: session.get(\"sessionMap\") && session.get(\"sessionMap\").url(),\n    location: session.get(\"sessionLocation\")\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_SESSIONS\", fromParseSessions);\n"
  },
  {
    "path": "js/reducers/surveys.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type Question = {\n  text: string,\n  lowLabel: string,\n  highLabel: string\n};\n\nexport type Survey = {\n  id: string,\n  sessionId: string,\n  questions: Array<Question>\n};\n\ntype State = Array<Survey>;\n\nfunction surveys(state: State = [], action: Action): State {\n  if (action.type === \"LOADED_SURVEYS\") {\n    return action.list;\n  }\n  if (action.type === \"SUBMITTED_SURVEY_ANSWERS\") {\n    const submittedSurveyId = action.id;\n    return state.filter(survey => survey.id !== submittedSurveyId);\n  }\n  if (action.type === \"LOGGED_OUT\") {\n    return [];\n  }\n  return state;\n}\n\nmodule.exports = surveys;\n"
  },
  {
    "path": "js/reducers/testEventDates.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\ntype State = ?number;\n\nfunction setCurrentDateForTesting(state: State = null, action: Action): State {\n  if (action.type === \"SET_TIMED_TESTING\") {\n    return action.value;\n  }\n  return state;\n}\n\nmodule.exports = setCurrentDateForTesting;\n"
  },
  {
    "path": "js/reducers/user.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\nexport type State = {\n  isLoggedIn: boolean,\n  hasSkippedLogin: boolean,\n  sharedSchedule: ?boolean,\n  id: ?string,\n  name: ?string\n};\n\nconst initialState = {\n  isLoggedIn: false,\n  hasSkippedLogin: false,\n  sharedSchedule: null,\n  id: null,\n  name: null\n};\n\nfunction user(state: State = initialState, action: Action): State {\n  if (action.type === \"LOGGED_IN\") {\n    let { id, name, sharedSchedule } = action.data;\n    if (sharedSchedule === undefined) {\n      sharedSchedule = null;\n    }\n    return {\n      isLoggedIn: true,\n      hasSkippedLogin: false,\n      sharedSchedule,\n      id,\n      name\n    };\n  }\n  if (action.type === \"SKIPPED_LOGIN\") {\n    return {\n      ...initialState,\n      hasSkippedLogin: true\n    };\n  }\n  if (action.type === \"LOGGED_OUT\") {\n    return initialState;\n  }\n  if (action.type === \"SET_SHARING\") {\n    return {\n      ...state,\n      sharedSchedule: action.enabled\n    };\n  }\n  if (action.type === \"RESET_NUXES\") {\n    return { ...state, sharedSchedule: null };\n  }\n  return state;\n}\n\nmodule.exports = user;\n"
  },
  {
    "path": "js/reducers/videoFilter.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport type { Action } from \"../actions/types\";\n\n// export type FriendFilter = {\n//   id: string;\n//   name: string;\n//   video: {[key: string]: boolean};\n// };\n\nexport type TopicsFilter = {\n  [key: string]: boolean\n};\n\ntype State = TopicsFilter;\n\nfunction filter(state: State = {}, action: Action): State {\n  if (action.type === \"APPLY_VIDEO_TOPICS_FILTER\") {\n    return action.videoTopics;\n  }\n  if (action.type === \"CLEAR_VIDEO_FILTER\") {\n    return {};\n  }\n  return state;\n}\n\nmodule.exports = filter;\n"
  },
  {
    "path": "js/reducers/videoTopics.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\ntype State = Array<string>;\ntype Action = { type: string, list: Array<any> };\n\nfunction topics(state: State = [], action: Action): State {\n  if (action.type === \"LOADED_VIDEOS\") {\n    const topicsMap = Object.create(null);\n    action.list.forEach(video => {\n      const tags = video.get(\"tags\") || [];\n      tags.forEach(tag => {\n        topicsMap[tag] = true;\n      });\n    });\n    return Object.keys(topicsMap).sort();\n  }\n  return state;\n}\n\nmodule.exports = topics;\n"
  },
  {
    "path": "js/reducers/videos.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport createParseReducer from \"./createParseReducer\";\n\nexport type Video = {\n  id: string,\n  source: string,\n  title: string,\n  description: string,\n  image: ?string,\n  year: number,\n  length: string,\n  tags: Array<string>,\n  shareURL: ?string,\n  featured: ?boolean\n  // survey/rating/review?\n};\n\nfunction fromParseVideos(video: Object): Video {\n  return {\n    id: video.id,\n    source: video.get(\"source\"),\n    title: video.get(\"title\"),\n    description: video.get(\"description\"),\n    image: video.get(\"image\"),\n    year: video.get(\"year\"),\n    length: video.get(\"length\"),\n    tags: video.get(\"tags\") || [],\n    shareURL: video.get(\"shareURL\"),\n    featured: video.get(\"featured\")\n  };\n}\n\nmodule.exports = createParseReducer(\"LOADED_VIDEOS\", fromParseVideos);\n"
  },
  {
    "path": "js/relay-environment.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport { Environment, Network, RecordSource, Store } from \"relay-runtime\";\n\nimport { graphqlURL } from \"./env\";\n\nconst source = new RecordSource();\nconst store = new Store(source);\n\nfunction fetchQuery(operation, variables, cacheConfig, uploadables) {\n  return fetch(graphqlURL, {\n    method: \"POST\",\n    headers: {\n      Accept: \"application/json\",\n      \"content-type\": \"application/json\"\n    },\n    body: JSON.stringify({\n      query: operation.text,\n      variables\n    })\n  }).then(response => {\n    return response.json();\n  });\n}\n\nconst network = Network.create(fetchQuery);\n\nmodule.exports = new Environment({\n  network,\n  store\n});\n"
  },
  {
    "path": "js/setup.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\n// Depdencies\nimport React from \"react\";\nimport FacebookSDK from \"./FacebookSDK\";\nimport Parse from \"parse/react-native\";\nimport configureStore from \"./store/configureStore\";\nimport { Provider } from \"react-redux\";\n\n// Components\nimport { Text, AsyncStorage } from \"react-native\";\nimport F8App from \"./F8App\";\nimport LaunchScreen from \"./common/LaunchScreen\";\n\n// Config\nimport { serverURL, parseAppID } from \"./env\";\n\nfunction setup(): ReactClass<{}> {\n  console.disableYellowBox = true;\n  Parse.setAsyncStorage(AsyncStorage);\n  Parse.initialize(parseAppID);\n  Parse.serverURL = `${serverURL}/parse`;\n  console.log(\"DEBUG!!! \" + serverURL);\n\n  FacebookSDK.init();\n  Parse.FacebookUtils.init();\n\n  // TODO: Don't prevent fontScaling on iOS (currently breaks UI)\n  Text.defaultProps.allowFontScaling = false;\n\n  class Root extends React.Component {\n    state: {\n      isLoading: boolean,\n      store: any\n    };\n\n    constructor() {\n      super();\n      this.state = {\n        storeCreated: false,\n        storeRehydrated: false,\n        store: null\n      };\n    }\n\n    componentDidMount() {\n      configureStore(\n        // rehydration callback (after async compatibility and persistStore)\n        _ => this.setState({ storeRehydrated: true })\n      ).then(\n        // creation callback (after async compatibility)\n        store => this.setState({ store, storeCreated: true })\n      );\n    }\n\n    render() {\n      if (!this.state.storeCreated || !this.state.storeRehydrated) {\n        return <LaunchScreen />;\n      }\n      return (\n        <Provider store={this.state.store}>\n          <F8App />\n        </Provider>\n      );\n    }\n  }\n\n  return Root;\n}\n\nmodule.exports = setup;\n"
  },
  {
    "path": "js/store/analytics.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport track from \"./track\";\n\nmodule.exports = store => next => action => {\n  track(action);\n  return next(action);\n};\n"
  },
  {
    "path": "js/store/array.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nmodule.exports = store => next => action =>\n  Array.isArray(action) ? action.map(next) : next(action);\n"
  },
  {
    "path": "js/store/compatibility.js",
    "content": "import { AsyncStorage } from \"react-native\";\nimport { compatibleStoreVersion } from \"../env\";\n\nconst LS_GROUP = \"F8StoreCompatibility\",\n  LS_VERSIONING = `${LS_GROUP}:version`;\n\nexport async function ensureCompatibility() {\n  try {\n    const stored = await AsyncStorage.getItem(LS_VERSIONING);\n    if (stored && stored === JSON.stringify(compatibleStoreVersion)) {\n      return false; // no need to update\n    }\n  } catch (error) {}\n  return await resetCompatibility();\n}\n\nasync function resetCompatibility() {\n  try {\n    const keys = await AsyncStorage.getAllKeys();\n    // force clear everything except for versioning (all reduxPersist:x and Parse:x keys)\n    const targets = (keys || []).filter(k => k !== LS_VERSIONING);\n    if (targets.length) {\n      await AsyncStorage.multiRemove(targets);\n    }\n    // after storage reset, update the compatibility to the current storage version\n    return await updateCompatibility();\n  } catch (error) {}\n  return false;\n}\n\nasync function updateCompatibility() {\n  try {\n    await AsyncStorage.setItem(\n      LS_VERSIONING,\n      JSON.stringify(compatibleStoreVersion)\n    );\n    return true;\n  } catch (error) {}\n  return false;\n}\n"
  },
  {
    "path": "js/store/configureStore.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport { applyMiddleware, createStore } from \"redux\";\nimport thunk from \"redux-thunk\";\nimport promise from \"./promise\";\nimport array from \"./array\";\nimport analytics from \"./analytics\";\nimport reducers from \"../reducers\";\nimport createLogger from \"redux-logger\";\nimport { persistStore, autoRehydrate } from \"redux-persist\";\nimport { AsyncStorage } from \"react-native\";\nimport { ensureCompatibility } from \"./compatibility\";\n\nconst isDebuggingInChrome = false;\n\nconst logger = createLogger({\n  predicate: (getState, action) => isDebuggingInChrome,\n  collapsed: true,\n  duration: true\n});\n\nconst createF8Store = applyMiddleware(thunk, promise, array, analytics, logger)(\n  createStore\n);\n\nasync function configureStore(onComplete: ?() => void) {\n  const didReset = await ensureCompatibility();\n  const store = autoRehydrate()(createF8Store)(reducers);\n  persistStore(store, { storage: AsyncStorage }, _ => onComplete(didReset));\n\n  if (isDebuggingInChrome) {\n    window.store = store;\n  }\n  return store;\n}\n\nmodule.exports = configureStore;\n"
  },
  {
    "path": "js/store/promise.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport { testMenuEnabled } from \"../env\";\n\nfunction warn(error) {\n  if (testMenuEnabled) {\n    console.warn(error.message || error);\n  } // only log promise failures when debug menu is enabled\n  throw error; // To let the caller handle the rejection\n}\n\nmodule.exports = store => next => action =>\n  typeof action.then === \"function\"\n    ? Promise.resolve(action).then(next, warn)\n    : next(action);\n"
  },
  {
    "path": "js/store/track.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport F8Analytics from \"../F8Analytics\";\nimport type { Action } from \"../actions/types\";\n\nfunction track(action: Action): void {\n  switch (action.type) {\n    case \"LOGGED_IN\":\n      F8Analytics.logEvent(\"Login\", 1, { source: action.source || \"\" });\n      break;\n\n    case \"LOGGED_OUT\":\n      F8Analytics.logEvent(\"Logout\", 1);\n      break;\n\n    case \"SKIPPED_LOGIN\":\n      F8Analytics.logEvent(\"Skip login\", 1);\n      break;\n\n    case \"SESSION_ADDED\":\n      F8Analytics.logEvent(\"Added To Schedule\", 1, { id: action.id });\n      break;\n\n    case \"SESSION_REMOVED\":\n      F8Analytics.logEvent(\"Removed From Schedule\", 1, { id: action.id });\n      break;\n\n    case \"TURNED_ON_PUSH_NOTIFICATIONS\":\n      F8Analytics.logEvent(\"Enabled Push\", 1);\n      break;\n\n    case \"SKIPPED_PUSH_NOTIFICATIONS\":\n      F8Analytics.logEvent(\"Disabled Push\", 1);\n      break;\n\n    case \"SET_SHARING\":\n      F8Analytics.logEvent(\n        action.enabled ? \"Enabled Sharing\" : \"Disabled Sharing\",\n        1\n      );\n      break;\n\n    case \"APPLY_SCHEDULE_TOPICS_FILTER\":\n      F8Analytics.logEvent(\"Filtered Schedule\", 1);\n      break;\n\n    case \"APPLY_VIDEO_TOPICS_FILTER\":\n      F8Analytics.logEvent(\"Filtered Videos\", 1);\n      break;\n  }\n}\n\nmodule.exports = track;\n"
  },
  {
    "path": "js/tabs/F8TabsView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\nimport type { Tab, Day } from \"../reducers/navigation\";\nimport { switchTab } from \"../actions\";\nimport unseenNotificationsCount from \"./notifications/unseenNotificationsCount\";\n\nimport F8Fonts from \"../common/F8Fonts\";\nimport F8Colors from \"../common/F8Colors\";\nimport StyleSheet from \"../common/F8StyleSheet\";\n\nimport {\n  PixelRatio,\n  Platform,\n  View,\n  Text,\n  Image,\n  StatusBar\n} from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport F8InfoView from \"./info/F8InfoView\";\nimport MyScheduleView from \"./schedule/MyScheduleView\";\nimport GeneralScheduleView from \"./schedule/GeneralScheduleView\";\nimport F8VideosView from \"./videos/F8VideosView\";\nimport TabNavigator from \"react-native-tab-navigator\";\nimport F8DemosView from \"./demos/F8DemosView\";\n\nimport { currentTimeOnConferenceDay } from \"../common/convertTimes\";\n\n/* constants\n============================================================================= */\n\nconst SCHEDULE_ICONS = {\n  day1: {\n    default: require(\"./schedule/img/tab-icon/1/default.png\"),\n    active: require(\"./schedule/img/tab-icon/1/active.png\")\n  },\n  day2: {\n    default: require(\"./schedule/img/tab-icon/2/default.png\"),\n    active: require(\"./schedule/img/tab-icon/2/active.png\")\n  }\n};\n\nconst BADGE_SIZE = 14,\n  BADGE_PADDING_H = 3,\n  UPDATE_LOOP_MINUTES = 1,\n  UPDATE_LOOP_DURATION = UPDATE_LOOP_MINUTES * 60 * 1000; // convert ms;\n\n/* =============================================================================\n<F8TabsView />\n--------------------------------------------------------------------------------\nProps:\n  coming soon\n\n============================================================================= */\n\nclass F8TabsView extends React.Component {\n  props: {\n    tab: Tab,\n    day: Day,\n    onTabSelect: (tab: Tab) => void,\n    navigator: Navigator\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      now: props.presetDate\n        ? currentTimeOnConferenceDay(props.presetDate)\n        : new Date().getTime()\n    };\n  }\n\n  componentDidMount() {\n    if (Platform.OS === \"ios\") {\n      StatusBar && StatusBar.setBarStyle(\"light-content\"); // TODO: VIDEO FIX?\n    }\n\n    clearInterval(this.updateLoop);\n    this.updateLoop = setInterval(_ => {\n      const now = this.props.presetDate\n        ? currentTimeOnConferenceDay(this.props.presetDate)\n        : new Date().getTime();\n      this.setState({ now });\n    }, UPDATE_LOOP_DURATION);\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (nextProps.presetDate !== this.props.presetDate) {\n      const now = nextProps.presetDate\n        ? currentTimeOnConferenceDay(nextProps.presetDate)\n        : new Date().getTime();\n      this.setState({ now });\n    }\n  }\n\n  componentWillUnmount() {\n    clearInterval(this.updateLoop);\n  }\n\n  onTabSelect(tab: Tab) {\n    if (this.props.tab !== tab) {\n      this.props.onTabSelect(tab);\n    }\n  }\n\n  render() {\n    let scheduleIcon = SCHEDULE_ICONS.day1.default; // day 1 and fallback\n    let scheduleIconSelected = SCHEDULE_ICONS.day1.active; // day 1 and fallback\n    if (this.props.day === 2) {\n      scheduleIcon = SCHEDULE_ICONS.day2.default;\n      scheduleIconSelected = SCHEDULE_ICONS.day2.active;\n    }\n\n    return (\n      <TabNavigator tabBarStyle={styles.tabBar}>\n        <TabNavigator.Item\n          title=\"Schedule\"\n          titleStyle={styles.tabTitle}\n          selectedTitleStyle={styles.tabTitleActive}\n          selected={this.props.tab === \"schedule\"}\n          onPress={this.onTabSelect.bind(this, \"schedule\")}\n          renderIcon={_ => this.renderTabIcon(scheduleIcon)}\n          renderSelectedIcon={_ => this.renderTabIcon(scheduleIconSelected)}\n        >\n          <GeneralScheduleView\n            now={this.state.now}\n            navigator={this.props.navigator}\n          />\n        </TabNavigator.Item>\n\n        <TabNavigator.Item\n          title=\"My F8\"\n          titleStyle={styles.tabTitle}\n          selectedTitleStyle={styles.tabTitleActive}\n          selected={this.props.tab === \"myF8\"}\n          onPress={this.onTabSelect.bind(this, \"myF8\")}\n          renderIcon={_ =>\n            this.renderTabIcon(\n              require(\"./schedule/img/tab-icon/my-f8/default.png\")\n            )}\n          renderSelectedIcon={_ =>\n            this.renderTabIcon(\n              require(\"./schedule/img/tab-icon/my-f8/active.png\")\n            )}\n        >\n          <MyScheduleView\n            now={this.state.now}\n            navigator={this.props.navigator}\n          />\n        </TabNavigator.Item>\n\n        <TabNavigator.Item\n          title=\"Demos\"\n          titleStyle={styles.tabTitle}\n          selectedTitleStyle={styles.tabTitleActive}\n          selected={this.props.tab === \"demos\"}\n          onPress={this.onTabSelect.bind(this, \"demos\")}\n          renderIcon={_ =>\n            this.renderTabIcon(require(\"./demos/img/tab-icon/default.png\"))}\n          renderSelectedIcon={_ =>\n            this.renderTabIcon(require(\"./demos/img/tab-icon/active.png\"))}\n        >\n          <F8DemosView navigator={this.props.navigator} />\n        </TabNavigator.Item>\n\n        <TabNavigator.Item\n          title=\"Videos\"\n          titleStyle={styles.tabTitle}\n          selectedTitleStyle={styles.tabTitleActive}\n          selected={this.props.tab === \"videos\"}\n          onPress={this.onTabSelect.bind(this, \"videos\")}\n          renderIcon={_ =>\n            this.renderTabIcon(require(\"./videos/img/tab-icon/default.png\"))}\n          renderSelectedIcon={_ =>\n            this.renderTabIcon(require(\"./videos/img/tab-icon/active.png\"))}\n        >\n          <F8VideosView navigator={this.props.navigator} />\n        </TabNavigator.Item>\n\n        <TabNavigator.Item\n          title=\"Information\"\n          titleStyle={styles.tabTitle}\n          selectedTitleStyle={styles.tabTitleActive}\n          selected={this.props.tab === \"info\"}\n          onPress={this.onTabSelect.bind(this, \"info\")}\n          renderBadge={_ => <TabBadge value={this.props.notificationsBadge} />}\n          renderIcon={_ =>\n            this.renderTabIcon(require(\"./info/img/tab-icon/default.png\"))}\n          renderSelectedIcon={_ =>\n            this.renderTabIcon(require(\"./info/img/tab-icon/active.png\"))}\n        >\n          <F8InfoView navigator={this.props.navigator} />\n        </TabNavigator.Item>\n      </TabNavigator>\n    );\n  }\n\n  renderTabIcon(src) {\n    return (\n      <View style={styles.iconWrapper}>\n        <Image style={styles.tabIcon} source={src} />\n      </View>\n    );\n  }\n}\n\nclass TabBadge extends React.Component {\n  render() {\n    if (!this.props.value) {\n      return null;\n    }\n    const len = String(this.props.value).length;\n    let sizing;\n    if (len > 1) {\n      sizing = styles.badgeFlexible;\n    } else {\n      sizing = styles.badgeFixed;\n    }\n\n    return (\n      <View style={[styles.badge, sizing]}>\n        <Text style={styles.badgeText}>{this.props.value}</Text>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  tabBar: {\n    borderTopWidth: 1 / PixelRatio.get(),\n    borderTopColor: F8Colors.magnesium,\n    backgroundColor: F8Colors.lightBackground\n  },\n  tabTitle: {\n    backgroundColor: \"transparent\",\n    // fontFamily: OC3Fonts.regular,\n    fontSize: 10,\n    color: F8Colors.colorWithAlpha(\"sapphire\", 0.65)\n  },\n  tabTitleActive: {\n    color: F8Colors.sapphire\n  },\n  badge: {\n    position: \"absolute\",\n    right: -5,\n    top: 2,\n    backgroundColor: F8Colors.pink,\n    // borderWidth:1,\n    // borderColor: F8Colors.pink,\n    borderRadius: BADGE_SIZE / 2,\n    height: BADGE_SIZE,\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  badgeFixed: {\n    width: BADGE_SIZE\n  },\n  badgeFlexible: {\n    paddingHorizontal: BADGE_PADDING_H\n  },\n  badgeText: {\n    backgroundColor: \"transparent\",\n    fontSize: 9,\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helveticaBold\"),\n    color: F8Colors.white,\n\n    ios: {\n      lineHeight: 10\n    }\n  },\n\n  // icons ===================\n\n  iconWrapper: {\n    width: 28,\n    height: 30,\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    bottom: -3\n    // backgroundColor:'rgba(0,0,0,1)',\n    // ios: { bottom: -3 },\n    // android: { bottom: -4 },\n  },\n\n  tabIcon: {\n    // backgroundColor:'rgba(255,255,0,1)',\n  }\n});\n\n/* Selectors\n============================================================================= */\n\nfunction select(store) {\n  return {\n    tab: store.navigation.tab,\n    day: store.navigation.day,\n    presetDate: store.testEventDates,\n    notificationsBadge: unseenNotificationsCount(store)\n  };\n}\n\nfunction actions(dispatch) {\n  return {\n    onTabSelect: tab => dispatch(switchTab(tab))\n  };\n}\n\n/* Export\n============================================================================= */\nmodule.exports = connect(select, actions)(F8TabsView);\n"
  },
  {
    "path": "js/tabs/MenuItem.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport React from \"react\";\nimport View from \"View\";\nimport { Text } from \"../common/F8Text\";\nimport F8Touchable from \"../../common/F8Touchable\";\nimport Image from \"Image\";\nimport StyleSheet from \"StyleSheet\";\n\nclass MenuItem extends React.Component {\n  props: {\n    icon: number,\n    selectedIcon: number,\n    selected: boolean,\n    title: string,\n    badge: ?string,\n    onPress: () => void\n  };\n\n  render() {\n    let icon = this.props.selected ? this.props.selectedIcon : this.props.icon;\n    const selectedTitleStyle = this.props.selected && styles.selectedTitle;\n    let badge;\n    if (this.props.badge) {\n      badge = (\n        <View style={styles.badge}>\n          <Text style={styles.badgeText}>{this.props.badge}</Text>\n        </View>\n      );\n    }\n    return (\n      <F8Touchable onPress={this.props.onPress}>\n        <View style={styles.container}>\n          <Image style={styles.icon} source={icon} />\n          <Text style={[styles.title, selectedTitleStyle]}>\n            {this.props.title}\n          </Text>\n          {badge}\n        </View>\n      </F8Touchable>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flexDirection: \"row\",\n    height: 50,\n    alignItems: \"center\",\n    paddingHorizontal: 20\n  },\n  icon: {\n    marginRight: 20\n  },\n  title: {\n    flex: 1,\n    fontSize: 17,\n    color: F8Colors.lightText\n  },\n  selectedTitle: {\n    color: F8Colors.darkText\n  },\n  badge: {\n    backgroundColor: \"#DC3883\",\n    paddingHorizontal: 10,\n    paddingVertical: 2,\n    borderRadius: 10\n  },\n  badgeText: {\n    fontSize: 12,\n    color: \"white\"\n  }\n});\n\nmodule.exports = MenuItem;\n"
  },
  {
    "path": "js/tabs/demos/DemosCarousel.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Analytics from \"../../F8Analytics\";\nimport { View } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport F8DemoDetails from \"./F8DemoDetails\";\nimport F8Header from \"../../common/F8Header\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport Carousel from \"../../common/Carousel\";\nimport { HeaderTitle } from \"../../common/F8Text\";\nimport F8PageControl from \"../../common/F8PageControl\";\n\nimport { connect } from \"react-redux\";\n\ntype Props = {\n  allDemos: Array<mixed>,\n  navigator: Navigator\n};\n\nclass DemosCarousel extends React.Component {\n  props: Props;\n  state: {\n    selectedIndex: number\n  };\n\n  static defaultProps = {\n    title: \"Demos\"\n  };\n\n  constructor(props) {\n    super(props);\n    this.state = {\n      selectedIndex: props.selectedIndex\n    };\n\n    (this: any).handleIndexChange = this.handleIndexChange.bind(this);\n    (this: any).renderCard = this.renderCard.bind(this);\n  }\n\n  render() {\n    const backItem = {\n      title: \"Back\",\n      layout: \"icon\",\n      icon: require(\"../../common/img/header/back.png\"),\n      onPress: () => this.props.navigator.pop()\n    };\n\n    return (\n      <View style={styles.container}>\n        <F8Header\n          backgroundColor={F8Colors.turquoise}\n          titleColor={F8Colors.sapphire}\n          navItem={backItem}\n        >\n          <View style={styles.headerContent}>\n            <HeaderTitle>{this.props.title}</HeaderTitle>\n            <F8PageControl\n              count={this.props.allDemos.length}\n              selectedIndex={this.state.selectedIndex}\n            />\n          </View>\n        </F8Header>\n        <Carousel\n          count={this.props.allDemos.length}\n          selectedIndex={this.state.selectedIndex}\n          onSelectedIndexChange={this.handleIndexChange}\n          renderCard={this.renderCard}\n        />\n      </View>\n    );\n  }\n\n  renderCard(index: number): ReactElement {\n    return (\n      <F8DemoDetails\n        navigator={this.props.navigator}\n        demo={this.props.allDemos[index]}\n      />\n    );\n  }\n\n  componentDidMount() {\n    this.track(this.state.selectedIndex);\n  }\n\n  handleIndexChange(selectedIndex: number) {\n    this.track(selectedIndex);\n    this.setState({ selectedIndex });\n  }\n\n  track(index: number) {\n    const { id } = this.props.allDemos[index];\n    F8Analytics.logEvent(\"View Demo\", 1, { id });\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.white\n  },\n  headerContent: {\n    android: {\n      flex: 1,\n      alignItems: \"flex-start\",\n      justifyContent: \"center\"\n    },\n    ios: {\n      alignItems: \"center\",\n      justifyContent: \"center\"\n    }\n  },\n  day: {\n    color: F8Colors.yellow,\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helveticaBold\"),\n    fontSize: 13\n  },\n  time: {\n    color: F8Colors.white,\n    fontFamily: F8Fonts.helvetica,\n    fontSize: 15\n  }\n});\n\nmodule.exports = connect()(DemosCarousel);\n"
  },
  {
    "path": "js/tabs/demos/F8DemoDetails.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\n\nimport {\n  Dimensions,\n  Image,\n  View,\n  TouchableOpacity,\n  ScrollView\n} from \"react-native\";\n\nimport { Text, Heading2, Heading4, Paragraph } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Button from \"../../common/F8Button\";\nimport ActionsOverlay from \"../../common/ActionsOverlay\";\nimport F8ScrollingHeader from \"../../common/F8ScrollingHeader\";\nimport MapView from \"../../common/MapView\";\nimport Carousel from \"../../common/Carousel\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  HORIZONTAL_BREAKPOINT = WINDOW_WIDTH <= 320,\n  CONTENT_PADDING_H = HORIZONTAL_BREAKPOINT ? 20 : 30;\n\n/* =============================================================================\n<F8DemoDetails />\n============================================================================= */\n\nconst F8DemoDetails = React.createClass({\n  getInitialState: function() {\n    return {\n      scrollTop: 0\n    };\n  },\n\n  render: function() {\n    const { demo, map } = this.props;\n    const paddingBottom = demo.booking ? 110 : 30;\n\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <ScrollView\n          contentContainerStyle={[styles.contentContainer, { paddingBottom }]}\n          onScroll={({ nativeEvent }) =>\n            this.setState({ scrollTop: nativeEvent.contentOffset.y })}\n          scrollEventThrottle={100}\n          showsVerticalScrollIndicator={false}\n          automaticallyAdjustContentInsets={false}\n        >\n          {this.renderHeading(demo)}\n          {this.renderDescription(demo.description)}\n          {this.renderMap(map)}\n          {this.renderLinksSection(demo.links)}\n        </ScrollView>\n        {this.renderActions(demo.booking)}\n        {this.renderMiniHeader(demo.title)}\n      </View>\n    );\n  },\n\n  renderHeading({ title, logo, logoWidth, logoHeight }) {\n    if (logo && logoWidth && logoHeight) {\n      return (\n        <Image\n          style={[styles.logo, { width: logoWidth, height: logoHeight }]}\n          source={{ uri: logo }}\n        />\n      );\n    } else if (title) {\n      return <Heading2 style={styles.title}>{title}</Heading2>;\n    } else {\n      return null;\n    }\n  },\n\n  renderDescription(description) {\n    if (description) {\n      return <Paragraph>{description}</Paragraph>;\n    } else {\n      return null;\n    }\n  },\n\n  renderMap(map) {\n    if (map) {\n      const mapWidth = Carousel.CardWidth - CONTENT_PADDING_H * 2;\n      return <MapView width={mapWidth} style={styles.map} map={map} />;\n    } else {\n      return null;\n    }\n  },\n\n  renderLinksSection(links) {\n    if (!links || !links.length) {\n      return null;\n    }\n\n    const content = links.map((link, idx) => {\n      if (link.title && link.url) {\n        return (\n          <TouchableOpacity\n            key={`${link.title}${idx}`}\n            style={styles.link}\n            onPress={_ =>\n              this.props.navigator &&\n              this.props.navigator.push({\n                webview: link.url,\n                backgroundColor: F8Colors.turquoise,\n                titleColor: F8Colors.white,\n                itemsColor: F8Colors.white\n              })}\n          >\n            <Text numberOfLines={1} style={styles.linkText}>\n              {link.title}\n            </Text>\n            <Image\n              style={styles.linkArrow}\n              source={require(\"../../common/img/pointer-right.png\")}\n            />\n          </TouchableOpacity>\n        );\n      }\n    });\n\n    return <Section title=\"More Information\">{content}</Section>;\n  },\n\n  // \"Book Now\" floating actions if necessary\n  renderActions(bookingURL) {\n    if (!bookingURL) {\n      return null;\n    }\n    return (\n      <ActionsOverlay\n        gradientColors={[\"rgba(255,255,255,0)\", \"rgba(255,255,255,1)\"]}\n        buttonContainerStyles={{ paddingHorizontal: 15, paddingBottom: 12 }}\n        style={styles.actions}\n      >\n        <F8Button\n          style={{ flex: 1 }}\n          caption=\"Book a Demo\"\n          onPress={_ => {\n            this.props.navigator &&\n              this.props.navigator.push({\n                webview: bookingURL,\n                backgroundColor: F8Colors.turquoise,\n                titleColor: F8Colors.white,\n                itemsColor: F8Colors.white\n              });\n          }}\n        />\n      </ActionsOverlay>\n    );\n  },\n\n  // Small header that shows/hides at scroll y offset trigger\n  renderMiniHeader(title) {\n    return (\n      <F8ScrollingHeader\n        contentInset={CONTENT_PADDING_H}\n        scrollTop={this.state.scrollTop}\n        text={title}\n      />\n    );\n  }\n});\n\nclass Section extends React.Component {\n  props: {\n    title?: string,\n    children?: any\n  };\n\n  render() {\n    const { children } = this.props;\n    if (React.Children.count(children) === 0) {\n      return null;\n    }\n    let header;\n    if (this.props.title) {\n      header = (\n        <Heading4 style={styles.sectionTitle}>\n          {this.props.title.toUpperCase()}\n        </Heading4>\n      );\n    }\n    return (\n      <View style={styles.section}>\n        {header}\n        {children}\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: \"white\"\n  },\n  contentContainer: {\n    paddingTop: 25,\n    paddingHorizontal: CONTENT_PADDING_H\n  },\n  title: {\n    color: F8Colors.blue,\n    marginTop: 5,\n    marginBottom: 20\n  },\n  logo: {\n    marginTop: 5,\n    marginBottom: 20\n  },\n  section: {\n    marginTop: 30\n  },\n  sectionTitle: {\n    marginBottom: 8\n  },\n  actions: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0\n  },\n  map: {\n    marginTop: 32\n  },\n\n  link: {\n    paddingVertical: 10,\n    flexDirection: \"row\",\n    alignItems: \"center\"\n  },\n  linkText: {\n    flex: 1,\n    paddingRight: 10,\n    alignSelf: \"flex-start\"\n  },\n  linkArrow: {\n    flex: 0,\n    alignSelf: \"flex-end\"\n  }\n});\n\n/* data store =============================================================== */\nfunction select(store, props) {\n  const map = store.maps.find(({ name }) => name === props.demo.location);\n  return {\n    map\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select)(F8DemoDetails);\n"
  },
  {
    "path": "js/tabs/demos/F8DemosView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { QueryRenderer, graphql } from \"react-relay\";\nimport idx from \"idx\";\nimport { connect } from \"react-redux\";\nimport ListContainer from \"../../common/ListContainer\";\nimport PureListView from \"../../common/PureListView\";\nimport F8Colors from \"../../common/F8Colors\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport { Dimensions, View, Image, TouchableOpacity } from \"react-native\";\nimport F8Button from \"../../common/F8Button\";\nimport { Heading2, Heading5, Text } from \"../../common/F8Text\";\nimport F8BackgroundRepeat from \"../../common/F8BackgroundRepeat\";\nimport environment from \"../../relay-environment\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  PADDING_HORIZONTAL = 12,\n  ILLUSTRATION_HEIGHT = 505,\n  ILLUSTRATION_VISIBLE = 36,\n  ILLUSTRATION_OFFSET = ILLUSTRATION_HEIGHT - ILLUSTRATION_VISIBLE,\n  PATTERN_VISIBLE = 12,\n  PATTERN_HEIGHT = ILLUSTRATION_HEIGHT - 25,\n  PATTERN_OFFSET = PATTERN_HEIGHT - PATTERN_VISIBLE;\n\n/**\n* ==============================================================================\n* <F8DemosView />\n* ------------------------------------------------------------------------------\n* @param {Array.<Demo>} demos Parse Demo class\n* @param {Config} config Parse config vars\n* @param {F8Navigator} navigator Navigation methods\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8DemosView extends React.Component {\n  constructor(props) {\n    super(props);\n\n    this.renderView = this.renderView.bind(this);\n    this.onRowSelect = this.onRowSelect.bind(this);\n  }\n\n  render() {\n    return (\n      <QueryRenderer\n        environment={environment}\n        query={graphql`\n          query F8DemosViewQuery {\n            demos {\n              title\n              description\n              booking\n              location\n              links {\n                title\n                url\n              }\n              logo\n              logoHeight\n              logoWidth\n              devGarage\n            }\n          }\n        `}\n        render={({ error, props }) => {\n          const sortedDemos = sortDemos(idx(props, _ => _.demos));\n          const demos = sortedDemos.filter(d => !d.devGarage);\n          const garages = sortedDemos.filter(d => d.devGarage);\n          const hasBookables = !!sortedDemos.find(d => d.booking);\n          return (\n            <ListContainer\n              headerBackgroundColor={F8Colors.turquoise}\n              headerTitleColor={F8Colors.sapphire}\n              title=\"Demos\"\n              leftItem={{\n                title: \"Map\",\n                layout: \"icon\",\n                icon: require(\"../../common/img/header/map.png\"),\n                onPress: _ =>\n                  this.props.navigator &&\n                  this.props.navigator.push({ maps: true })\n              }}\n            >\n              <PureListView\n                renderRow={_ => {}}\n                renderEmptyList={() =>\n                  this.renderView(demos, garages, hasBookables)}\n              />\n            </ListContainer>\n          );\n        }}\n      />\n    );\n  }\n\n  renderView(demos, garages, hasBookables) {\n    const hasBothTables = demos.length && garages.length;\n\n    return (\n      <View style={styles.container}>\n        <View style={styles.header}>\n          <F8BackgroundRepeat\n            width={WINDOW_WIDTH}\n            height={PATTERN_HEIGHT}\n            source={require(\"../../common/img/pattern-dots.png\")}\n            style={styles.headerBackground}\n          />\n          <Image\n            style={styles.headerIllustration}\n            source={require(\"./img/header.png\")}\n          />\n        </View>\n        <View style={styles.contentContainer}>\n          <Heading2 style={styles.mainHeading}>\n            {\"Here are the demos\\nyou’ll find at F8.\"}\n          </Heading2>\n          {this.renderTable(demos, hasBothTables ? \"Demos\" : undefined)}\n          {this.renderTable(\n            this.props.garages,\n            hasBothTables ? \"Developer Garage\" : undefined\n          )}\n          {this.renderManageReservationsButton(hasBookables)}\n        </View>\n      </View>\n    );\n  }\n\n  renderTable(rows: Array<mixed> = [], groupTitle: ?string) {\n    if (rows.length) {\n      const tableHeading = groupTitle ? (\n        <Heading5 style={styles.tableHeading}>\n          {groupTitle.toUpperCase()}\n        </Heading5>\n      ) : null;\n      return (\n        <View style={styles.section}>\n          {tableHeading}\n          <View style={styles.table}>\n            {rows.map((cell, index) => {\n              const isFirst = index === 0;\n              const { title, booking } = cell;\n              let bookableFlag;\n              if (booking) {\n                bookableFlag = (\n                  <Image\n                    style={styles.bookable}\n                    source={require(\"./img/bookable-flag.png\")}\n                  />\n                );\n              }\n              const dividerStyles = isFirst ? null : styles.tableRowDivider;\n              return (\n                <View style={[styles.tableRow, dividerStyles]} key={title}>\n                  {bookableFlag}\n                  <TouchableOpacity\n                    style={{\n                      flex: 1,\n                      alignItems: \"center\",\n                      justifyContent: \"center\",\n                      paddingHorizontal: 20\n                    }}\n                    onPress={_ => this.onRowSelect(index, rows, groupTitle)}\n                  >\n                    <Text style={styles.tableRowLabel}>{title}</Text>\n                  </TouchableOpacity>\n                </View>\n              );\n            })}\n          </View>\n        </View>\n      );\n    } else {\n      return null;\n    }\n  }\n\n  renderManageReservationsButton(hasBookables) {\n    if (hasBookables && this.props.config.manageBookingsURL) {\n      return (\n        <F8Button\n          opacity={0.5}\n          theme=\"bordered\"\n          caption=\"Manage Reservations\"\n          onPress={_ =>\n            this.props.navigator &&\n            this.props.navigator.push({\n              webview: this.props.config.manageBookingsURL,\n              backgroundColor: F8Colors.turquoise,\n              titleColor: F8Colors.white,\n              itemsColor: F8Colors.white\n            })}\n        />\n      );\n    } else {\n      return null;\n    }\n  }\n\n  onRowSelect(selectedIndex: number, rows: Array<mixed>, title: ?string) {\n    this.props.navigator &&\n      this.props.navigator.push({\n        allDemos: rows,\n        selectedIndex,\n        title\n      });\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {},\n  contentContainer: {\n    paddingTop: 11,\n    paddingHorizontal: PADDING_HORIZONTAL,\n    paddingBottom: 30\n  },\n  header: {\n    alignItems: \"center\"\n  },\n  headerBackground: {\n    position: \"absolute\",\n    left: 0,\n    top: -PATTERN_OFFSET,\n    right: 0\n  },\n  headerIllustration: {\n    marginTop: -ILLUSTRATION_OFFSET\n  },\n  mainHeading: {\n    textAlign: \"center\",\n    color: F8Colors.blue,\n    marginTop: 10,\n    marginBottom: 13\n  },\n  section: {\n    marginTop: 10,\n    marginBottom: 19\n  },\n  tableHeading: {\n    textAlign: \"center\",\n    color: F8Colors.pink\n  },\n  table: {\n    marginTop: 10,\n    borderRadius: 2,\n    borderWidth: 1,\n    borderColor: \"rgba(0,0,0,0.15)\",\n    backgroundColor: F8Colors.white\n  },\n  tableRow: {\n    height: 60\n  },\n  tableRowDivider: {\n    borderTopWidth: 1,\n    borderColor: F8Colors.colorWithAlpha(\"black\", 0.15)\n  },\n  tableRowLabel: {\n    fontSize: 17,\n    textAlign: \"center\",\n    color: F8Colors.tangaroa,\n    backgroundColor: \"transparent\",\n\n    android: {\n      paddingBottom: 5\n    }\n  },\n  bookable: {\n    position: \"absolute\",\n    right: 0,\n    top: 0,\n    width: 86,\n    height: 56\n  }\n});\n\n/* redux select ============================================================= */\n\nfunction sortDemos(demos = []) {\n  const other = [],\n    pinned = [];\n  (demos || []).map(demo => {\n    if (demo.booking) {\n      pinned.push(demo);\n    } else {\n      other.push(demo);\n    }\n  });\n  return [...pinned, ...other];\n}\n\nfunction select(store) {\n  return {\n    config: store.config\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select)(F8DemosView);\n"
  },
  {
    "path": "js/tabs/demos/__generated__/F8DemosViewQuery.graphql.js",
    "content": "/**\n * @flow\n * @relayHash d1f7b51cfc6f9fe392e405fc2f8b3967\n */\n\n/* eslint-disable */\n\n\"use strict\";\n\n/*::\nimport type {ConcreteBatch} from 'relay-runtime';\nexport type F8DemosViewQueryResponse = {|\n  +demos: ?$ReadOnlyArray<?{|\n    +title: ?string;\n    +description: ?string;\n    +booking: ?string;\n    +location: ?string;\n    +links: ?$ReadOnlyArray<?{|\n      +title: ?string;\n      +url: ?string;\n    |}>;\n    +logo: ?string;\n    +logoHeight: ?number;\n    +logoWidth: ?number;\n    +devGarage: ?boolean;\n  |}>;\n|};\n*/\n\n/*\nquery F8DemosViewQuery {\n  demos {\n    title\n    description\n    booking\n    location\n    links {\n      title\n      url\n    }\n    logo\n    logoHeight\n    logoWidth\n    devGarage\n    id\n  }\n}\n*/\n\nconst batch /*: ConcreteBatch*/ = {\n  fragment: {\n    argumentDefinitions: [],\n    kind: \"Fragment\",\n    metadata: null,\n    name: \"F8DemosViewQuery\",\n    selections: [\n      {\n        kind: \"LinkedField\",\n        alias: null,\n        args: null,\n        concreteType: \"Demo\",\n        name: \"demos\",\n        plural: true,\n        selections: [\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"title\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"description\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"booking\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"location\",\n            storageKey: null\n          },\n          {\n            kind: \"LinkedField\",\n            alias: null,\n            args: null,\n            concreteType: \"DemoLink\",\n            name: \"links\",\n            plural: true,\n            selections: [\n              {\n                kind: \"ScalarField\",\n                alias: null,\n                args: null,\n                name: \"title\",\n                storageKey: null\n              },\n              {\n                kind: \"ScalarField\",\n                alias: null,\n                args: null,\n                name: \"url\",\n                storageKey: null\n              }\n            ],\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logo\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logoHeight\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logoWidth\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"devGarage\",\n            storageKey: null\n          }\n        ],\n        storageKey: null\n      }\n    ],\n    type: \"Query\"\n  },\n  id: null,\n  kind: \"Batch\",\n  metadata: {},\n  name: \"F8DemosViewQuery\",\n  query: {\n    argumentDefinitions: [],\n    kind: \"Root\",\n    name: \"F8DemosViewQuery\",\n    operation: \"query\",\n    selections: [\n      {\n        kind: \"LinkedField\",\n        alias: null,\n        args: null,\n        concreteType: \"Demo\",\n        name: \"demos\",\n        plural: true,\n        selections: [\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"title\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"description\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"booking\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"location\",\n            storageKey: null\n          },\n          {\n            kind: \"LinkedField\",\n            alias: null,\n            args: null,\n            concreteType: \"DemoLink\",\n            name: \"links\",\n            plural: true,\n            selections: [\n              {\n                kind: \"ScalarField\",\n                alias: null,\n                args: null,\n                name: \"title\",\n                storageKey: null\n              },\n              {\n                kind: \"ScalarField\",\n                alias: null,\n                args: null,\n                name: \"url\",\n                storageKey: null\n              }\n            ],\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logo\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logoHeight\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"logoWidth\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"devGarage\",\n            storageKey: null\n          },\n          {\n            kind: \"ScalarField\",\n            alias: null,\n            args: null,\n            name: \"id\",\n            storageKey: null\n          }\n        ],\n        storageKey: null\n      }\n    ]\n  },\n  text:\n    \"query F8DemosViewQuery {\\n  demos {\\n    title\\n    description\\n    booking\\n    location\\n    links {\\n      title\\n      url\\n    }\\n    logo\\n    logoHeight\\n    logoWidth\\n    devGarage\\n    id\\n  }\\n}\\n\"\n};\n\nmodule.exports = batch;\n"
  },
  {
    "path": "js/tabs/info/AboutLocation.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { View } from \"react-native\";\nimport { Paragraph, Heading4 } from \"../../common/F8Text\";\nimport F8Colors from \"../../common/F8Colors\";\nimport DirectionsLink from \"./DirectionsLink\";\n\ntype Props = {\n  title: string,\n  date: string,\n  venue: string,\n  address: string\n};\n\ntype State = {};\n\n/**\n* ==============================================================================\n* <AboutLocation />\n* ------------------------------------------------------------------------------\n* @param {?string} title Section title\n* @param {?string} date Formatted date text\n* @param {?string} venue Venue name\n* @param {string} address Address used for opening maps\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nexport default class AboutLocation extends React.Component {\n  props: Props;\n  state: State = {};\n\n  render() {\n    return (\n      <View style={this.props.style}>\n        {this.renderTitle()}\n        {this.renderDate()}\n        {this.renderVenue()}\n        {this.renderAddress()}\n      </View>\n    );\n  }\n\n  renderTitle() {\n    if (this.props.title) {\n      return <Heading4>{this.props.title.toUpperCase()}</Heading4>;\n    } else {\n      return null;\n    }\n  }\n\n  renderDate() {\n    if (this.props.date) {\n      return <Paragraph>{this.props.date}</Paragraph>;\n    } else {\n      return null;\n    }\n  }\n\n  renderVenue() {\n    if (this.props.venue) {\n      return <Paragraph>{this.props.venue}</Paragraph>;\n    } else {\n      return null;\n    }\n  }\n\n  renderAddress() {\n    if (this.props.address) {\n      return (\n        <DirectionsLink address={this.props.address.replace(\"\\n\", \" \")}>\n          <Paragraph\n            style={{ color: F8Colors.blue, textDecorationLine: \"underline\" }}\n          >\n            {this.props.address}\n          </Paragraph>\n        </DirectionsLink>\n      );\n    } else {\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "js/tabs/info/CommonQuestions.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport F8Linking from \"../../common/F8Linking\";\nimport { StyleSheet, View, Text } from \"react-native\";\nimport { Heading4, Paragraph } from \"../../common/F8Text\";\nimport Hyperlink from \"react-native-hyperlink\";\nimport F8Colors from \"../../common/F8Colors\";\n\nclass CommonQuestions extends React.Component {\n  render() {\n    let content = this.props.faqs.map(({ question, answer }) => (\n      <Row question={question} answer={answer} key={question} />\n    ));\n    return (\n      <View style={this.props.style}>\n        {this.renderTitle()}\n        {content}\n      </View>\n    );\n  }\n\n  renderTitle() {\n    if (this.props.title) {\n      return <Heading4>{this.props.title.toUpperCase()}</Heading4>;\n    } else {\n      return null;\n    }\n  }\n}\n\nclass Row extends React.Component {\n  render() {\n    return (\n      <View style={styles.row}>\n        <Hyperlink\n          linkStyle={styles.hyperlink}\n          onPress={url =>\n            F8Linking.canOpenURL(url).then(supported => {\n              if (supported) {\n                F8Linking.openURL(url);\n              }\n            })}\n        >\n          <Paragraph>\n            <Text style={styles.question}>{this.props.question} </Text>\n            <Text>{this.props.answer}</Text>\n          </Paragraph>\n        </Hyperlink>\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  row: {\n    marginVertical: 15\n  },\n  question: {\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\")\n  },\n  hyperlink: {\n    color: F8Colors.blue,\n    textDecorationLine: \"underline\",\n    textDecorationColor: F8Colors.blue\n  }\n});\n\nmodule.exports = CommonQuestions;\n"
  },
  {
    "path": "js/tabs/info/DirectionsLink.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Platform, TouchableOpacity, ActionSheetIOS } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport F8Linking from \"../../common/F8Linking\";\n\n/**\n* ==============================================================================\n* <DirectionsLink />\n* ------------------------------------------------------------------------------\n* @param {string} address Full address to open with selected maps app\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass DirectionsLink extends React.Component {\n  static defaultProps = {\n    address: \"\"\n  };\n\n  render() {\n    return (\n      <TouchableOpacity onPress={this.onPress}>\n        {this.props.children}\n      </TouchableOpacity>\n    );\n  }\n\n  onPress = _ => {\n    if (Platform.OS === \"ios\") {\n      ActionSheetIOS.showActionSheetWithOptions(\n        {\n          title: this.props.address,\n          options: [\"Open in Apple Maps\", \"Open in Google Maps\", \"Cancel\"],\n          destructiveButtonIndex: -1,\n          cancelButtonIndex: 2\n        },\n        this.openMaps\n      );\n    } else {\n      // android\n      let address = encodeURIComponent(this.props.address);\n      F8Linking.openURL(\"https://maps.google.com/maps?&q=\" + address);\n    }\n  };\n\n  openMaps = option => {\n    const address = encodeURIComponent(this.props.address);\n    switch (option) {\n      case 0:\n        F8Linking.openURL(\"https://maps.apple.com/?q=\" + address);\n        break;\n\n      case 1:\n        const nativeGoogleUrl =\n          \"comgooglemaps-x-callback://?q=\" +\n          address +\n          \"&x-success=f8://&x-source=F8\";\n        F8Linking.canOpenURL(nativeGoogleUrl).then(supported => {\n          const url = supported\n            ? nativeGoogleUrl\n            : \"https://maps.google.com/?q=\" + address;\n          F8Linking.openURL(url);\n        });\n        break;\n    }\n  };\n}\n\n/* playground cards ========================================================= */\n\nconst directionsLink = DirectionsLink;\ndirectionsLink.__cards__ = define => {\n  define(\"Large Blue/Green\", _ => (\n    <DirectionsLink address=\"150 West San Carlos Street San Jose, CA 95113\">\n      <Text>{\"150 West San Carlos Street\\nSan Jose, CA 95113\"}</Text>\n    </DirectionsLink>\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = directionsLink;\n"
  },
  {
    "path": "js/tabs/info/F8AboutView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { View, Image, Dimensions } from \"react-native\";\nimport { HorizontalRule } from \"../../common/F8Text\";\nimport LinksList from \"./LinksList\";\nimport CommonQuestions from \"./CommonQuestions\";\nimport WiFiDetails from \"./WiFiDetails\";\nimport AboutLocation from \"./AboutLocation\";\n\n/* constants ================================================================ */\n\nconst PADDING_HORIZONTAL = 18,\n  WINDOW_WIDTH = Dimensions.get(\"window\").width;\n\n/* <F8AboutView />\nprops: navigator (for webview), pages, policies, faqs, config\n============================================================================= */\n\nclass F8AboutView extends React.Component {\n  constructor() {\n    super();\n\n    this.webview = this.webview.bind(this);\n  }\n\n  webview(url) {\n    this.props.navigator && this.props.navigator.push({ webview: url });\n  }\n\n  render() {\n    const imageW = WINDOW_WIDTH - PADDING_HORIZONTAL * 2;\n    const imageH = imageW / (375 / 115);\n\n    return (\n      <View style={{ paddingBottom: 80 }}>\n        <Image\n          key={\"AV_HeaderImage\"}\n          source={require(\"./img/hello-world.png\")}\n          style={{\n            width: imageW,\n            height: imageH,\n            marginTop: 30,\n            marginBottom: 50,\n            marginHorizontal: PADDING_HORIZONTAL,\n            resizeMode: \"contain\"\n          }}\n        />\n\n        <AboutLocation\n          key={\"AV_AboutLocation\"}\n          style={{ paddingHorizontal: PADDING_HORIZONTAL }}\n          title=\"Facebook Developer Conference\"\n          date=\"April 18 + 19, 2017\"\n          venue=\"San Jose Convention Center\"\n          address={\"150 West San Carlos Street\\nSan Jose, CA 95113\"}\n        />\n\n        {this.renderWiFiDetailsSection()}\n        {this.renderFAQSection()}\n        {this.renderPagesSection()}\n        {this.renderPoliciesSection()}\n        {this.renderThirdPartyNoticesSection()}\n      </View>\n    );\n  }\n\n  renderWiFiDetailsSection() {\n    const { config } = this.props;\n    if (config && config.wifiNetwork && config.wifiPassword) {\n      return [\n        <HorizontalRule\n          key={\"AV_HR_WiFI\"}\n          style={{\n            marginVertical: 30,\n            marginHorizontal: PADDING_HORIZONTAL\n          }}\n        />,\n        <WiFiDetails\n          key={\"AV_WiFI\"}\n          network={config.wifiNetwork}\n          password={config.wifiPassword}\n        />\n      ];\n    } else {\n      return null;\n    }\n  }\n\n  renderFAQSection() {\n    const { faqs } = this.props;\n\n    if (faqs && faqs.length) {\n      return [\n        <HorizontalRule\n          key={\"AV_HR_CommonQs\"}\n          style={{\n            marginVertical: 30,\n            marginHorizontal: PADDING_HORIZONTAL\n          }}\n        />,\n        <CommonQuestions\n          key={\"AV_CommonQs\"}\n          title=\"Frequently Asked Questions\"\n          faqs={this.props.faqs}\n          style={{\n            paddingHorizontal: PADDING_HORIZONTAL\n          }}\n        />\n      ];\n    } else {\n      return null;\n    }\n  }\n\n  renderPagesSection() {\n    const { pages } = this.props;\n\n    if (pages && pages.length) {\n      return [\n        <HorizontalRule\n          key={\"AV_HR_Pages\"}\n          style={{\n            marginVertical: 30,\n            marginHorizontal: PADDING_HORIZONTAL\n          }}\n        />,\n        <LinksList\n          key={\"AV_Pages\"}\n          title=\"Facebook pages\"\n          links={this.props.pages}\n          onSelect={this.webview}\n        />\n      ];\n    } else {\n      return null;\n    }\n  }\n\n  renderPoliciesSection() {\n    const { policies } = this.props;\n\n    if (policies && policies.length) {\n      return [\n        <HorizontalRule\n          key={\"AV_HR_Policies\"}\n          style={{\n            marginVertical: 30,\n            marginHorizontal: PADDING_HORIZONTAL\n          }}\n        />,\n        <LinksList\n          key={\"AV_Policies\"}\n          title=\"Policies & Notices\"\n          links={this.props.policies}\n          onSelect={this.webview}\n        />\n      ];\n    } else {\n      return null;\n    }\n  }\n\n  renderThirdPartyNoticesSection() {\n    const { config } = this.props;\n    const title = \"Third Party Notices\";\n    if (config && config.thirdPartyNotices) {\n      return [\n        <HorizontalRule\n          key={\"AV_HR_TPN\"}\n          style={{\n            marginVertical: 30,\n            marginHorizontal: PADDING_HORIZONTAL\n          }}\n        />,\n        <LinksList\n          key={\"AV_TPN\"}\n          title={title}\n          onSelect={this.webview}\n          links={[{ title, url: config.thirdPartyNotices }]}\n        />\n      ];\n    } else {\n      return null;\n    }\n  }\n}\n\n/* export =================================================================== */\nexport default F8AboutView;\n"
  },
  {
    "path": "js/tabs/info/F8InfoView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\nimport ListContainer from \"../../common/ListContainer\";\nimport PureListView from \"../../common/PureListView\";\nimport F8AboutView from \"./F8AboutView\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8NotificationsView from \"../notifications/F8NotificationsView\";\nimport { Platform, ActionSheetIOS } from \"react-native\";\nimport unseenNotificationsCount from \"../notifications/unseenNotificationsCount\";\nimport { testMenuEnabled, version } from \"../../env\";\nimport { TEST_MENU } from \"../../actions\";\n\n/* <F8InfoView />\n============================================================================= */\n\nclass F8InfoView extends React.Component {\n  render() {\n    return (\n      <ListContainer\n        headerBackgroundColor={F8Colors.salmon}\n        title=\"Information\"\n        leftItem={{\n          title: \"Map\",\n          layout: \"icon\",\n          icon: require(\"../../common/img/header/map.png\"),\n          onPress: _ =>\n            this.props.navigator && this.props.navigator.push({ maps: true })\n        }}\n        {...this.renderTestItems()}\n      >\n        <PureListView\n          title=\"About\"\n          renderRow={_ => {}}\n          renderEmptyList={_ => (\n            <F8AboutView\n              navigator={this.props.navigator}\n              config={this.props.config}\n              pages={this.props.pages}\n              faqs={this.props.faqs}\n              policies={this.props.policies}\n            />\n          )}\n        />\n        <F8NotificationsView\n          title=\"Notifications\"\n          hasUpdates={this.props.notificationsBadge}\n          navigator={this.props.navigator}\n        />\n      </ListContainer>\n    );\n  }\n\n  renderTestItems() {\n    if (!testMenuEnabled) {\n      return {};\n    }\n\n    if (Platform.OS === \"ios\") {\n      return {\n        rightItem: {\n          title: \"Test\",\n          onPress: () => this.showTestMenu()\n        }\n      };\n    }\n\n    if (Platform.OS === \"android\") {\n      return {\n        extraItems: Object.keys(TEST_MENU).map(title => ({\n          title,\n          onPress: () => this.props.dispatch(TEST_MENU[title]())\n        }))\n      };\n    }\n  }\n\n  showTestMenu() {\n    const itemTitles = Object.keys(TEST_MENU);\n    ActionSheetIOS.showActionSheetWithOptions(\n      {\n        title: \"Testing F8 app v\" + version,\n        options: [\"Cancel\", ...itemTitles],\n        cancelButtonIndex: 0\n      },\n      idx => {\n        if (idx === 0) {\n          return;\n        }\n\n        const action: any = TEST_MENU[itemTitles[idx - 1]];\n        this.props.dispatch(action());\n      }\n    );\n  }\n}\n\n/* redux store ============================================================== */\n\nfunction select(store) {\n  return {\n    config: store.config,\n    faqs: store.faqs,\n    pages: store.pages,\n    policies: store.policies,\n    notificationsBadge: unseenNotificationsCount(store) + store.surveys.length\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select)(F8InfoView);\n"
  },
  {
    "path": "js/tabs/info/LinksList.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Touchable from \"../../common/F8Touchable\";\nimport F8Linking from \"../../common/F8Linking\";\nimport { View, Image, StyleSheet } from \"react-native\";\nimport { Text, Heading4 } from \"../../common/F8Text\";\n\n/* constants\n============================================================================= */\nconst ICON_SIZE = 45;\n\n/* <LinksList />\n============================================================================= */\n\nclass LinksList extends React.Component {\n  props: {\n    title: string,\n    links: Array<{\n      logo?: ?string,\n      title: string,\n      url?: string,\n      onPress?: () => void\n    }>\n  };\n\n  constructor() {\n    super();\n\n    this.onSelectRow = this.onSelectRow.bind(this);\n  }\n\n  onSelectRow(url, title) {\n    this.props.onSelect && this.props.onSelect(url, title);\n  }\n\n  render() {\n    let content = this.props.links.map(link => (\n      <Row onSelect={this.onSelectRow} link={link} key={link.title} />\n    ));\n    return (\n      <View style={this.props.style}>\n        {this.renderTitle()}\n        {content}\n      </View>\n    );\n  }\n\n  renderTitle() {\n    if (this.props.title) {\n      return (\n        <Heading4 style={styles.heading}>\n          {this.props.title.toUpperCase()}\n        </Heading4>\n      );\n    } else {\n      return null;\n    }\n  }\n}\n\nclass Row extends React.Component {\n  props: {\n    link: {\n      logo: ?string,\n      title: string,\n      url?: string,\n      onPress?: () => void\n    }\n  };\n\n  render() {\n    const { logo, title } = this.props.link;\n    const image = logo && (\n      <Image\n        resizeMode=\"contain\"\n        style={styles.rowIcon}\n        source={{ uri: logo }}\n      />\n    );\n    const rowHeight = image ? styles.tallRow : undefined;\n\n    return (\n      <F8Touchable onPress={this.handlePress.bind(this)}>\n        <View style={[styles.row, rowHeight]}>\n          {image}\n          <Text style={styles.rowTitle} numberOfLines={2}>\n            {title}\n          </Text>\n          <Image source={require(\"../../common/img/pointer-right.png\")} />\n        </View>\n      </F8Touchable>\n    );\n  }\n\n  handlePress() {\n    const { onSelect, link } = this.props;\n    const { url, title } = link;\n    // open in embedded web view\n    if (onSelect) {\n      onSelect(url, title);\n    } else if (url) {\n      F8Linking.openURL(url);\n    }\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  heading: {\n    paddingHorizontal: 18,\n    marginBottom: 20\n  },\n  row: {\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    // paddingVertical: 8,\n    paddingRight: 16,\n    marginLeft: 12,\n    height: 54\n  },\n  tallRow: {\n    height: 62 // could be 63\n  },\n  rowIcon: {\n    width: ICON_SIZE,\n    height: ICON_SIZE,\n    marginRight: 15\n  },\n  rowTitle: {\n    paddingLeft: 6,\n    color: F8Colors.tangaroa,\n    fontSize: 17,\n    flex: 1\n  },\n  button: {\n    padding: 10\n  },\n  like: {\n    // letterSpacing: 1,\n    // color: F8Colors.actionText,\n    // fontSize: 12,\n  }\n});\n\n/* Exports\n============================================================================= */\nmodule.exports = LinksList;\n"
  },
  {
    "path": "js/tabs/info/Section.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, View } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\n\nclass Section extends React.Component {\n  props: {\n    title: string,\n    children?: any,\n    style?: any\n  };\n\n  render() {\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <View style={styles.header}>\n          <Text style={styles.title}>{this.props.title}</Text>\n        </View>\n        {this.props.children}\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    paddingTop: 60,\n    paddingBottom: 0,\n    backgroundColor: \"white\"\n  },\n  header: {\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    marginBottom: 30\n  },\n  title: {\n    fontSize: 24,\n    fontWeight: \"bold\"\n  }\n});\n\nmodule.exports = Section;\n"
  },
  {
    "path": "js/tabs/info/WiFiDetails.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { View } from \"react-native\";\nimport { Paragraph, Heading4 } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\n\n/* Config\n============================================================================= */\n\ntype Props = {\n  network: string,\n  password: string\n};\n\ntype State = {};\n\n/* =============================================================================\n<WiFiDetails />\n============================================================================= */\n\nclass WiFiDetails extends React.Component {\n  props: Props;\n  state: State = {};\n\n  render() {\n    const { network, password } = this.props;\n\n    return (\n      <View style={styles.container}>\n        <Column\n          style={styles.colNetwork}\n          label=\"Wi-Fi Network\"\n          value={network}\n        />\n        <Column style={styles.colPassword} label=\"Password\" value={password} />\n      </View>\n    );\n  }\n}\n\nclass Column extends React.Component {\n  props: {\n    label: string,\n    value: string\n    // style\n  };\n\n  render() {\n    return (\n      <View style={this.props.style}>\n        <Heading4 style={styles.label}>\n          {this.props.label.toUpperCase()}\n        </Heading4>\n        <Paragraph style={styles.value}>{this.props.value}</Paragraph>\n      </View>\n    );\n  }\n}\n\n/* Styles\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    paddingTop: 0,\n    flexDirection: \"row\"\n  },\n  colNetwork: {\n    paddingHorizontal: 18\n  },\n  colPassword: {\n    paddingHorizontal: 18\n  }\n});\n\n/* Export\n============================================================================= */\nmodule.exports = WiFiDetails;\n"
  },
  {
    "path": "js/tabs/maps/F8MapView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport { Dimensions, View } from \"react-native\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Button from \"../../common/F8Button\";\nimport ListContainer from \"../../common/ListContainer\";\nimport MapView from \"../../common/MapView\";\nimport ActionsOverlay from \"../../common/ActionsOverlay\";\n\n// static height calculations\nimport F8Header from \"../../common/F8Header\";\n\nconst HEADER_HEIGHT = F8Header.height,\n  WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  WINDOW_HEIGHT = Dimensions.get(\"window\").height,\n  CONTROLS_HEIGHT = 90,\n  MAP_HEIGHT = WINDOW_HEIGHT - HEADER_HEIGHT;\n\nclass F8MapView extends React.Component {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      map: props.map1,\n      width: WINDOW_WIDTH,\n      height: MAP_HEIGHT\n    };\n  }\n\n  render() {\n    const { map } = this.state;\n    const { map1, map2 } = this.props;\n\n    return (\n      <ListContainer title=\"Map\" headerBackgroundColor={F8Colors.purple}>\n        <View style={styles.container}>\n          <MapView\n            width={WINDOW_WIDTH}\n            height={MAP_HEIGHT}\n            zoomable={true}\n            map={map}\n          />\n          <ActionsOverlay\n            gradientColors={[\n              F8Colors.colorWithAlpha(\"bianca\", 0),\n              F8Colors.bianca\n            ]}\n            style={styles.nav}\n            buttonContainerStyles={styles.navBtnContainer}\n          >\n            <F8Button\n              theme={map.name === map1.name ? \"maps\" : \"mapsInactive\"}\n              type=\"small\"\n              caption={map1.name}\n              onPress={_ => this.switchMap(map1.name)}\n            />\n            <F8Button\n              theme={map.name === map2.name ? \"maps\" : \"mapsInactive\"}\n              type=\"small\"\n              caption={map2.name}\n              onPress={_ => this.switchMap(map2.name)}\n            />\n            <F8Button\n              style={{ position: \"absolute\", right: 14, top: 0 }}\n              theme=\"blue\"\n              type=\"round\"\n              icon={require(\"../../common/img/buttons/icon-x.png\")}\n              onPress={_ => this.props.navigator && this.props.navigator.pop()}\n            />\n          </ActionsOverlay>\n        </View>\n      </ListContainer>\n    );\n  }\n\n  switchMap(mapName) {\n    const { map1, map2 } = this.props;\n    const { map } = this.state;\n    if (mapName === map1.name && map.name !== map1.name) {\n      this.setState({ map: map1 });\n    } else if (mapName === map2.name && map.name !== map2.name) {\n      this.setState({ map: map2 });\n    }\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1\n  },\n  nav: {\n    position: \"absolute\",\n    height: CONTROLS_HEIGHT,\n    left: 0,\n    bottom: 0,\n    right: 0\n  },\n  navBtnContainer: {\n    height: 64,\n    alignSelf: \"stretch\",\n    paddingTop: 8,\n    paddingLeft: 14,\n    paddingRight: 52 + 14,\n    justifyContent: \"center\"\n  }\n});\n\nfunction select(store) {\n  return {\n    map1: store.maps.find(map => map.name === \"Street Level\"),\n    map2: store.maps.find(map => map.name === \"Upper Level\")\n  };\n}\n\nmodule.exports = connect(select)(F8MapView);\n"
  },
  {
    "path": "js/tabs/maps/F8VenueMap.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport {\n  ScrollView,\n  Image,\n  Dimensions,\n  Platform,\n  PixelRatio\n} from \"react-native\";\nimport PhotoView from \"react-native-photo-view\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Colors from \"../../common/F8Colors\";\n\n/* Config\n============================================================================= */\n\nconst SCREEN_WIDTH = Dimensions.get(\"window\").width,\n  SCREEN_HEIGHT = Dimensions.get(\"window\").height;\n\n/* =============================================================================\n<F8VenueMap />\n============================================================================= */\n\nclass F8VenueMap extends React.Component {\n  static defaultProps = {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n    maximumZoomScale: 2\n  };\n\n  render() {\n    const { map } = this.props;\n    if (!map) {\n      return null;\n    }\n    const url = urlForMap(map);\n    const { width, height } = map;\n\n    return Platform.OS === \"ios\"\n      ? this.renderIOS(url, width, height)\n      : this.renderAndroid(url);\n  }\n\n  renderAndroid(url) {\n    return (\n      <PhotoView\n        source={{ uri: url }}\n        style={{ width: this.props.width, height: this.props.height }}\n      />\n    );\n  }\n\n  renderIOS(url, width, height) {\n    const contentHeight = this.props.height,\n      contentWidth = contentHeight * (width / height);\n    // minScale = Math.min(SCREEN_WIDTH/contentWidth, 1);\n    return (\n      <ScrollView\n        style={[\n          styles.container,\n          this.props.style,\n          {\n            width: this.props.width,\n            height: this.props.height\n          }\n        ]}\n        horizontal={true}\n        directionalLockEnabled={false}\n        scrollEventThrottle={100}\n        // onScroll={this.onZoomChanged}\n        showsHorizontalScrollIndicator={false}\n        showsVerticalScrollIndicator={false}\n        // minimumZoomScale={ minScale }\n        maximumZoomScale={this.props.maximumZoomScale}\n      >\n        <Image\n          style={{ width: contentWidth, height: contentHeight }}\n          source={{ uri: url }}\n        />\n      </ScrollView>\n    );\n  }\n}\n\nfunction urlForMap(map) {\n  if (!map) {\n    return \"\";\n  }\n  switch (PixelRatio.get()) {\n    case 1:\n      return map.x1url;\n    case 2:\n      return map.x2url;\n    case 3:\n      return map.x3url;\n  }\n  return map.x3url;\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    backgroundColor: F8Colors.bianca\n  }\n});\n\n/* Export\n============================================================================= */\nmodule.exports = F8VenueMap;\n"
  },
  {
    "path": "js/tabs/maps/ZoomableImage.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport Image from \"Image\";\nimport React from \"react\";\nimport ScrollView from \"ScrollView\";\nimport { StyleSheet } from \"react-native\";\nimport TouchableWithoutFeedback from \"TouchableWithoutFeedback\";\n\nclass ZoomableImage extends React.Component {\n  props: {\n    url: string\n  };\n  state: {\n    lastTapTimestamp: number,\n    isZoomed: boolean\n  };\n\n  constructor() {\n    super();\n    this.state = {\n      lastTapTimestamp: 0,\n      isZoomed: false\n    };\n\n    (this: any).onZoomChanged = this.onZoomChanged.bind(this);\n    (this: any).toggleZoom = this.toggleZoom.bind(this);\n  }\n\n  render() {\n    return (\n      <ScrollView\n        ref={c => (this._zoomableScroll = c)}\n        onScroll={this.onZoomChanged}\n        scrollEventThrottle={100}\n        scrollsToTop={false}\n        alwaysBounceVertical={false}\n        alwaysBounceHorizontal={false}\n        automaticallyAdjustContentInsets={false}\n        showsHorizontalScrollIndicator={false}\n        showsVerticalScrollIndicator={false}\n        maximumZoomScale={4}\n        centerContent={true}\n        contentContainerStyle={{ flex: 1 }}\n      >\n        <TouchableWithoutFeedback onPress={this.toggleZoom}>\n          <Image style={styles.image} source={{ uri: this.props.url }} />\n        </TouchableWithoutFeedback>\n      </ScrollView>\n    );\n  }\n\n  toggleZoom(e: any) {\n    const timestamp = new Date().getTime();\n    if (timestamp - this.state.lastTapTimestamp <= 500) {\n      const { locationX, locationY } = e.nativeEvent;\n      const size = this.state.isZoomed\n        ? { width: 10000, height: 10000 }\n        : { width: 0, height: 0 };\n      this._zoomableScroll.scrollResponderZoomTo({\n        x: locationX,\n        y: locationY,\n        ...size\n      });\n    }\n    this.setState({ lastTapTimestamp: timestamp });\n  }\n\n  onZoomChanged(e: any) {\n    this.setState({ isZoomed: e.nativeEvent.zoomScale > 1 });\n  }\n}\n\nconst styles = StyleSheet.create({\n  image: {\n    flex: 1,\n    resizeMode: Image.resizeMode.contain\n  }\n});\n\nmodule.exports = ZoomableImage;\n"
  },
  {
    "path": "js/tabs/notifications/F8NotificationsView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport EmptySchedule from \"../schedule/EmptySchedule\";\nimport PushNUXModal from \"./PushNUXModal\";\nimport PureListView from \"../../common/PureListView\";\nimport React from \"react\";\nimport NotificationCell from \"./NotificationCell\";\nimport allNotifications from \"./allNotifications\";\nimport findSessionByURI from \"./findSessionByURI\";\nimport { connect } from \"react-redux\";\nimport { turnOnPushNotifications, skipPushNotifications } from \"../../actions\";\nimport { createSelector } from \"reselect\";\n\nimport { View } from \"react-native\";\nimport F8TimelineBackground from \"../../common/F8TimelineBackground\";\nimport F8Linking from \"../../common/F8Linking\";\n\n/* <F8NotificationsView />\n============================================================================= */\n\nclass F8NotificationsView extends React.Component {\n  constructor(props) {\n    super(props);\n\n    (this: any).renderRow = this.renderRow.bind(this);\n    (this: any).renderEmptyList = this.renderEmptyList.bind(this);\n    (this: any).openNotification = this.openNotification.bind(this);\n    (this: any).openReview = this.openReview.bind(this);\n  }\n\n  render() {\n    return (\n      <View style={{ flex: 1 }}>\n        <PureListView\n          data={this.props.notifications}\n          renderEmptyList={this.renderEmptyList}\n          renderRow={this.renderRow}\n          renderFooter={_ => <F8TimelineBackground height={80} left={24} />}\n          showsVerticalScrollIndicator={false}\n        />\n        <PushNUXModal\n          visible={this.props.nux}\n          animationType=\"fade\"\n          transparent={true}\n          onTurnOnNotifications={this.props.onTurnOnNotifications}\n          onSkipNotifications={this.props.onSkipNotifications}\n        />\n      </View>\n    );\n  }\n\n  renderRow(notification, sid, rid) {\n    return (\n      <NotificationCell\n        key={notification.id}\n        notification={notification}\n        onPress={() => this.openNotification(notification)}\n        firstRow={rid === 0 || rid === \"0\"}\n      />\n    );\n  }\n\n  renderEmptyList(containerHeight) {\n    if (containerHeight === 0) {\n      return null;\n    }\n    return (\n      <EmptySchedule\n        style={{ height: containerHeight }}\n        title=\"Stay tuned!\"\n        text=\"Important updates and announcements will appear here\"\n      />\n    );\n  }\n\n  openNotification(notification) {\n    if (notification.url) {\n      const session = findSessionByURI(this.props.sessions, notification.url);\n      if (session) {\n        this.props.navigator.push({ session });\n      } else {\n        F8Linking.openURL(notification.url);\n        // this.props.navigator.push({ webview: notification.url }); // uses default theme\n      }\n    } else if (notification.survey) {\n      this.props.navigator.push({\n        rate: 1,\n        survey: notification.survey\n      });\n    }\n  }\n\n  openReview() {\n    this.props.navigator.push({\n      rate: 1,\n      surveys: this.props.surveys\n    });\n  }\n}\n\n/* redux ==================================================================== */\n\nconst data = createSelector(\n  allNotifications,\n  store => store.surveys,\n  store => store.notifications.enabled,\n  store => store.sessions,\n  (notifications, surveys, enabled, sessions) => {\n    const updatedSurveys = surveys.map(survey => {\n      const surveySession = sessions.find(s => s.id === survey.sessionId);\n      return {\n        text: `How was \"${surveySession.title}\"?`,\n        time: survey.time,\n        survey\n      };\n    });\n    return [...updatedSurveys, ...notifications].sort(function(a, b) {\n      return b.time - a.time;\n    });\n  }\n);\n\nfunction select(state) {\n  return {\n    nux: state.notifications.enabled === null,\n    notifications: data(state),\n    sessions: state.sessions\n  };\n}\n\nfunction actions(dispatch) {\n  return {\n    onTurnOnNotifications: () => dispatch(turnOnPushNotifications()),\n    onSkipNotifications: () => dispatch(skipPushNotifications()),\n    dispatch\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select, actions)(F8NotificationsView);\n"
  },
  {
    "path": "js/tabs/notifications/NotificationCell.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8SessionCell from \"../schedule/F8SessionCell\";\nimport React from \"react\";\nimport findSessionByURI from \"./findSessionByURI\";\nimport TouchableHighlight from \"../../common/F8Touchable\";\nimport moment from \"moment\";\nimport { connect } from \"react-redux\";\n\nimport { Dimensions, View, Image } from \"react-native\";\nimport { Text, Paragraph, Heading3 } from \"../../common/F8Text\";\nimport F8TimelineSegment from \"../../common/F8TimelineSegment\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Button from \"../../common/F8Button\";\n\n/* constants ================================================================ */\n\nconst IMAGE_RATIO = 230 / 375,\n  CELL_PADDING_VERTICAL = 14,\n  CELL_PADDING_LEFT = 50,\n  CELL_PADDING_RIGHT = 34,\n  TIMELINE_LEFT = 24,\n  TIMELINE_DOT_OFFSET_TOP = CELL_PADDING_VERTICAL, // + 3?\n  IMG_MARGIN_TOP = 18,\n  SCREEN_WIDTH = Dimensions.get(\"window\").width;\n\nconst ICON_TYPES = {\n  // matches names in getType()\n  default: {\n    defaultIcon: require(\"./img/timeline/triangle.png\"),\n    activeIcon: require(\"./img/timeline/triangle-active.png\")\n  },\n  session: {\n    defaultIcon: require(\"./img/timeline/circle.png\"),\n    activeIcon: require(\"./img/timeline/circle-active.png\")\n  },\n  survey: {\n    defaultIcon: require(\"./img/timeline/circle.png\"),\n    activeIcon: require(\"./img/timeline/circle-active.png\")\n  },\n  image: {\n    defaultIcon: require(\"./img/timeline/square.png\"),\n    activeIcon: require(\"./img/timeline/square-active.png\")\n  },\n  link: {\n    defaultIcon: require(\"./img/timeline/circle.png\"),\n    activeIcon: require(\"./img/timeline/circle-active.png\")\n  }\n};\n\n/* =============================================================================\n<NotificationCell />\n--------------------------------------------------------------------------------\n\nProps:\n  ! notification:object\n  ! onPress:function\n\n============================================================================= */\n\nclass NotificationCell extends React.Component {\n  render() {\n    const notificationType = this.getType();\n\n    const content = (\n      <View\n        style={[\n          styles.cell,\n          this.props.firstRow ? styles.firstCell : null,\n          this.props.style\n        ]}\n      >\n        {this.renderTimelineSegment(notificationType)}\n        {this.renderTimeAndText()}\n        {this.renderAttachmentByType(notificationType)}\n      </View>\n    );\n\n    return this.props.notification.url || this.props.notification.survey ? (\n      <TouchableHighlight onPress={this.props.onPress}>\n        {content}\n      </TouchableHighlight>\n    ) : (\n      content\n    );\n  }\n\n  renderTimelineSegment(type: string) {\n    const { defaultIcon, activeIcon } = ICON_TYPES[type];\n    const timelineTopOffset = this.props.firstRow ? TIMELINE_DOT_OFFSET_TOP : 0;\n\n    return (\n      <F8TimelineSegment\n        left={TIMELINE_LEFT}\n        lineOffsetTop={timelineTopOffset}\n        dotSize={13}\n        dotOffsetTop={TIMELINE_DOT_OFFSET_TOP}\n        dotIconDefault={defaultIcon}\n        dotIconActive={activeIcon}\n        active={!this.props.isSeen}\n      />\n    );\n  }\n\n  renderTimeAndText() {\n    const { notification } = this.props;\n    return [\n      <Text key={`${notification.id}_time`} style={styles.time}>\n        {moment(notification.time)\n          .fromNow()\n          .toUpperCase()}\n      </Text>,\n      <Paragraph key={`${notification.id}_text`} style={styles.text}>\n        {notification.text}\n      </Paragraph>\n    ];\n  }\n\n  renderAttachmentByType(type: string) {\n    if (type === \"image\") {\n      return this.renderImageAttachment();\n    } else if (type === \"session\") {\n      return this.renderSessionAttachment();\n    } else if (type === \"survey\") {\n      return this.renderSurveyAttachment();\n    } else if (type === \"link\") {\n      return this.renderLinkAttachment();\n    } else {\n      return null;\n    }\n  }\n\n  renderImageAttachment() {\n    const { notification } = this.props;\n    const imageW = SCREEN_WIDTH;\n    const imageH = imageW * IMAGE_RATIO;\n\n    let imageCTAButton;\n    if (notification.urlTitle) {\n      const watchIcon =\n        notification.urlTitle.toLowerCase().indexOf(\"watch\") > -1\n          ? require(\"../../common/img/buttons/play-medium.png\")\n          : null;\n      imageCTAButton = (\n        <F8Button\n          icon={watchIcon}\n          theme=\"yellow\"\n          caption={notification.urlTitle}\n        />\n      );\n    }\n\n    return (\n      <View style={[styles.image, { width: imageW, height: imageH }]}>\n        <Image\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: 0,\n            width: imageW,\n            height: imageH\n          }}\n          source={{ uri: notification.image }}\n        />\n        {imageCTAButton}\n      </View>\n    );\n  }\n\n  renderSessionAttachment() {\n    return (\n      <F8SessionCell\n        style={[styles.embeddedCard, styles.embeddedSession]}\n        embedded={true}\n        session={this.props.session}\n        showStartEndTime={true}\n      />\n    );\n  }\n\n  renderSurveyAttachment() {\n    return (\n      <View style={[styles.embeddedCard, styles.embeddedSurvey]}>\n        <Heading3 style={{ flex: 1, marginRight: 30 }}>Leave a review</Heading3>\n        <Image\n          style={{ position: \"absolute\", right: 14, top: 11 }}\n          source={require(\"./img/stars.png\")}\n        />\n      </View>\n    );\n  }\n\n  renderLinkAttachment() {\n    const { notification } = this.props;\n    const linkText = notification.url\n      .replace(\"https://\", \"\")\n      .replace(\"http://\", \"\");\n    const linkTitle = notification.urlTitle ? (\n      <Heading3>{notification.urlTitle}</Heading3>\n    ) : null;\n\n    return (\n      <View style={[styles.embeddedCard, styles.embeddedLink]}>\n        {linkTitle}\n        <Text numberOfLines={1} style={styles.url}>\n          {linkText}\n        </Text>\n      </View>\n    );\n  }\n\n  getType() {\n    const { notification, session } = this.props;\n    if (session) {\n      return \"session\";\n    } else if (notification.survey) {\n      return \"survey\";\n    } else if (notification.image) {\n      return \"image\";\n    } else if (notification.url) {\n      return \"link\";\n    } else {\n      return \"default\";\n    }\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  cell: {\n    paddingVertical: CELL_PADDING_VERTICAL,\n    paddingLeft: CELL_PADDING_LEFT,\n    paddingRight: CELL_PADDING_RIGHT\n  },\n  firstCell: {\n    marginTop: 15\n  },\n  time: {\n    fontFamily: F8Fonts.helvetica,\n    color: F8Colors.colorWithAlpha(\"black\", 0.5),\n    fontSize: 13,\n    marginBottom: 5\n  },\n  text: {\n    lineHeight: 22\n  },\n  embeddedCard: {\n    marginTop: 15,\n    borderWidth: 1,\n    borderRadius: 2,\n    borderColor: F8Colors.tangaroa,\n    backgroundColor: F8Colors.white\n  },\n  embeddedSession: {\n    paddingTop: 12,\n    paddingBottom: 14\n  },\n  embeddedLink: {\n    padding: 18\n  },\n  embeddedSurvey: {\n    paddingVertical: 15,\n    paddingHorizontal: 18,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"space-between\"\n  },\n  url: {\n    // flex: 1,\n    color: F8Colors.blue,\n    fontSize: 13,\n    marginTop: 3\n    // marginBottom: 10,\n  },\n  image: {\n    marginTop: IMG_MARGIN_TOP,\n    marginLeft: -CELL_PADDING_LEFT,\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  }\n});\n\n/* data store =============================================================== */\n\nfunction select(store, props) {\n  return {\n    session: findSessionByURI(store.sessions, props.notification.url),\n    isSeen: store.notifications.seen[props.notification.id]\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select)(NotificationCell);\n"
  },
  {
    "path": "js/tabs/notifications/PushNUXModal.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Button from \"../../common/F8Button\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Dimensions, View, Image, StyleSheet } from \"react-native\";\nimport { Heading2, Paragraph } from \"../../common/F8Text\";\nimport F8BackgroundRepeat from \"../../common/F8BackgroundRepeat\";\nimport F8Modal from \"../../common/F8Modal\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  MODAL_PADDING_H = 10,\n  MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2;\n\n/* <PushNUXModal />\n============================================================================= */\n\nclass PushNUXModal extends React.Component {\n  props: {\n    onTurnOnNotifications: () => void,\n    onSkipNotifications: () => void\n  };\n\n  render() {\n    return <F8Modal renderContent={this.renderContent} {...this.props} />;\n  }\n\n  renderContent = _ => {\n    return (\n      <View>\n        <View>\n          <F8BackgroundRepeat\n            width={MODAL_WIDTH}\n            height={124}\n            source={require(\"../../common/img/pattern-dots.png\")}\n            style={styles.headerBackground}\n          />\n          <Image\n            style={styles.headerIllustration}\n            source={require(\"./img/nux-header.png\")}\n          />\n        </View>\n        <View style={styles.content}>\n          <Heading2>Dont miss out!</Heading2>\n          <Paragraph style={styles.text}>\n            Turn on push notifications to see what’s happening at F8. You can\n            always see in-app updates on this tab.\n          </Paragraph>\n          <F8Button\n            style={styles.button}\n            type=\"primary\"\n            caption=\"Turn on push notifications\"\n            onPress={this.props.onTurnOnNotifications}\n          />\n          <F8Button\n            style={styles.button}\n            theme=\"bordered\"\n            opacity={0.5}\n            caption=\"Maybe later\"\n            onPress={this.props.onSkipNotifications}\n          />\n        </View>\n      </View>\n    );\n  };\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  headerBackground: {\n    position: \"absolute\",\n    left: 0,\n    top: 0\n  },\n  headerIllustration: {\n    alignSelf: \"center\"\n  },\n  content: {\n    paddingTop: 32,\n    paddingHorizontal: 20,\n    paddingBottom: 45,\n    alignItems: \"center\"\n  },\n  text: {\n    textAlign: \"center\",\n    paddingTop: 6,\n    paddingHorizontal: 10,\n    paddingBottom: 35,\n    fontSize: 15,\n    lineHeight: 24,\n    color: F8Colors.colorWithAlpha(\"tangaroa\", 0.7)\n  },\n  button: {\n    marginTop: 9,\n    alignSelf: \"stretch\"\n  }\n});\n\n/* exports ================================================================== */\nmodule.exports = PushNUXModal;\n"
  },
  {
    "path": "js/tabs/notifications/RateSessionsCell.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { StyleSheet, TouchableOpacity } from \"react-native\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Text } from \"../../common/F8Text\";\n\ntype Props = {\n  numberOfSessions: number,\n  onPress: () => void\n};\n\n/* <RateSessionsCell />\n============================================================================= */\n\nfunction RateSessionsCell({ numberOfSessions, onPress }: Props) {\n  const label = `Rate the session${numberOfSessions === 1\n    ? \"\"\n    : \"s\"} you attended`;\n  return (\n    <TouchableOpacity\n      style={styles.cell}\n      accessibilityTraits=\"button\"\n      onPress={onPress}\n    >\n      <Text style={styles.text}>{label.toUpperCase()}</Text>\n    </TouchableOpacity>\n  );\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  cell: {\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    padding: 15,\n    backgroundColor: F8Colors.yellow\n  },\n  text: {\n    fontSize: 13,\n    fontFamily: F8Fonts.helvetica,\n    color: F8Colors.pink\n  }\n});\n\n/* exports\n============================================================================= */\nmodule.exports = RateSessionsCell;\n"
  },
  {
    "path": "js/tabs/notifications/allNotifications.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport { createSelector } from \"reselect\";\n\nimport type { Notification } from \"../../reducers/notifications\";\n\n// Merges lists of notifications from server and notifications\n// received via push and makes sure there is no duplicates.\nfunction mergeAndSortByTime(\n  server: Array<Notification>,\n  push: Array<Notification>\n): Array<Notification> {\n  const uniquePush = push.filter(pushNotification => {\n    const existsOnServer = server.find(\n      serverNotification => serverNotification.text === pushNotification.text\n    );\n    return !existsOnServer;\n  });\n\n  const all = [].concat(server, uniquePush);\n  return all.sort((a, b) => b.time - a.time);\n}\n\nmodule.exports = createSelector(\n  store => store.notifications.server,\n  store => store.notifications.push,\n  mergeAndSortByTime\n);\n"
  },
  {
    "path": "js/tabs/notifications/findSessionByURI.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport type { Session } from \"../../reducers/sessions\";\n\nfunction findSessionByURI(sessions: Array<Session>, uri: ?string): ?Session {\n  if (!uri) {\n    return null;\n  }\n  const slug = uri.replace(\"f8://\", \"\");\n  for (let i = 0; i < sessions.length; i++) {\n    const session = sessions[i];\n    if (session.slug === slug || session.id === slug) {\n      return session;\n    }\n  }\n  return null;\n}\n\nmodule.exports = findSessionByURI;\n"
  },
  {
    "path": "js/tabs/notifications/unseenNotificationsCount.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport allNotifications from \"./allNotifications\";\nimport { createSelector } from \"reselect\";\n\nimport type {\n  Notification,\n  SeenNotifications\n} from \"../../reducers/notifications\";\n\nfunction unseenNotificationsCount(\n  notifications: Array<Notification>,\n  seen: SeenNotifications\n): number {\n  return notifications.filter(notification => !seen[notification.id]).length;\n}\n\nmodule.exports = createSelector(\n  allNotifications,\n  store => store.notifications.seen,\n  unseenNotificationsCount\n);\n"
  },
  {
    "path": "js/tabs/schedule/AddToScheduleButton.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { Animated } from \"react-native\";\nimport F8Button from \"../../common/F8Button\";\n\ntype Props = {\n  isAdded: boolean,\n  onPress: () => void,\n  addedImageSource?: ?string,\n  style?: any\n};\n\ntype State = {\n  anim: Animated.Value\n};\n\nconst SAVED_LABEL = \"Added to my F8\";\nconst ADD_LABEL = \"Add to my F8\";\n\nclass AddToScheduleButton extends React.Component {\n  props: Props;\n  state: State;\n\n  constructor(props: Props) {\n    super(props);\n    this.state = {\n      anim: new Animated.Value(props.isAdded ? 1 : 0)\n    };\n  }\n\n  render() {\n    const { isAdded, addedImageSource, style } = this.props;\n    const buttonTheme = isAdded ? \"yellow\" : \"pink\";\n    const caption = isAdded ? SAVED_LABEL : ADD_LABEL;\n    const icon =\n      isAdded && addedImageSource\n        ? addedImageSource\n        : require(\"./img/added.png\");\n\n    return (\n      <F8Button\n        style={style}\n        icon={icon}\n        caption={caption}\n        theme={buttonTheme}\n        onPress={this.props.onPress}\n      />\n    );\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    if (this.props.isAdded !== nextProps.isAdded) {\n      const toValue = nextProps.isAdded ? 1 : 0;\n      Animated.spring(this.state.anim, { toValue }).start();\n    }\n  }\n}\n\nmodule.exports = AddToScheduleButton;\n// $FlowFixMe\nmodule.exports.__cards__ = define => {\n  let f;\n  setInterval(() => f && f(), 1000);\n\n  define(\"Inactive\", (state, update) => (\n    <AddToScheduleButton isAdded={false} onPress={() => update(!state)} />\n  ));\n\n  define(\"Active\", (state, update) => (\n    <AddToScheduleButton isAdded={true} onPress={() => update(!state)} />\n  ));\n\n  define(\"Animated\", (state = false, update) => {\n    f = () => update(!state);\n    return <AddToScheduleButton isAdded={state} onPress={() => {}} />;\n  });\n};\n"
  },
  {
    "path": "js/tabs/schedule/EmptySchedule.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Paragraph, Heading3 } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport { View, Image } from \"react-native\";\nimport F8Colors from \"../../common/F8Colors\";\n\nclass EmptySchedule extends React.Component {\n  props: {\n    style?: any,\n    title?: string,\n    titleStyles?: any,\n    image?: number,\n    text: string,\n    textStyles?: any,\n    children?: any\n  };\n\n  render() {\n    const image = this.props.image && (\n      <Image style={styles.image} source={this.props.image} />\n    );\n    const title = this.props.title && (\n      <Heading3 style={[styles.title, this.props.titleStyles]}>\n        {this.props.title}\n      </Heading3>\n    );\n\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <View style={styles.content}>\n          {image}\n          {title}\n          <Paragraph style={[styles.text, this.props.textStyles]}>\n            {this.props.text}\n          </Paragraph>\n          {this.props.children}\n        </View>\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    justifyContent: \"center\"\n  },\n  content: {\n    padding: 30,\n    alignItems: \"center\"\n  },\n  image: {\n    marginBottom: 20\n  },\n  title: {\n    color: F8Colors.blue,\n    textAlign: \"center\",\n    marginBottom: 10\n  },\n  text: {\n    textAlign: \"center\",\n    marginBottom: 35\n  }\n});\n\nmodule.exports = EmptySchedule;\n"
  },
  {
    "path": "js/tabs/schedule/F8FriendGoing.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport ProfilePicture from \"../../common/ProfilePicture\";\nimport React from \"react\";\nimport { Image, StyleSheet, View } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport F8Touchable from \"../../common/F8Touchable\";\n\nimport type { FriendsSchedule } from \"../../reducers/friendsSchedules\";\n\nclass F8FriendGoing extends React.Component {\n  props: {\n    onPress: () => void,\n    friend: FriendsSchedule\n  };\n\n  render() {\n    return (\n      <F8Touchable onPress={this.props.onPress}>\n        <View style={styles.container}>\n          <ProfilePicture userID={this.props.friend.id} size={29} />\n          <Text style={styles.name}>{this.props.friend.name}</Text>\n          <Image source={require(\"../../common/img/pointer-right.png\")} />\n        </View>\n      </F8Touchable>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    paddingVertical: 10,\n    backgroundColor: \"white\"\n  },\n  name: {\n    marginLeft: 12,\n    fontSize: 17,\n    flex: 1\n  }\n});\n\nmodule.exports = F8FriendGoing;\n"
  },
  {
    "path": "js/tabs/schedule/F8GanttGrid.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n * @flow\n */\n\"use strict\";\n\nimport moment from \"moment-timezone\";\nimport React from \"react\";\nimport { View, StyleSheet } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport { timezone } from \"../../env.js\";\n\n/* constants ================================================================ */\nconst LABELS_WIDTH = 30,\n  LABELS_HEIGHT = 22;\n\n/* =============================================================================\n<F8GanttGrid />\n============================================================================= */\n\nexport default class F8GanttGrid extends React.Component {\n  render() {\n    const ms = moment.tz(this.props.startTime, timezone),\n      me = moment.tz(this.props.endTime, timezone);\n    const diff = me.diff(ms, \"hours\");\n    const cols = this.renderColumns(ms, diff);\n    return (\n      <View style={[styles.gridContainer, { height: this.props.height }]}>\n        {cols}\n      </View>\n    );\n  }\n\n  renderColumns(momentStart: moment, count: number) {\n    let cols = [];\n    let previousLabelAMPM = null;\n    for (let i = 0; i < count + 1; i++) {\n      const left = this.props.containerWidth / count * i - LABELS_WIDTH / 2;\n      let label = null;\n      if (i % 2 === 0) {\n        let labelText = momentStart.add(i, \"h\").format(\"h\");\n        let ampm = momentStart\n          .format(\"A\")\n          .split(\"M\")\n          .join(\"\");\n        if (previousLabelAMPM && ampm !== previousLabelAMPM) {\n          labelText += momentStart\n            .format(\"A\")\n            .split(\"M\")\n            .join(\"\");\n        }\n        // if(ampm !== previousLabelAMPM) labelText += (momentStart.format('A')).split('M').join('');\n        previousLabelAMPM = momentStart\n          .format(\"A\")\n          .split(\"M\")\n          .join(\"\");\n        label = <Text style={styles.gridColumnLabel}>{labelText}</Text>;\n        momentStart.subtract(i, \"h\");\n      }\n      cols.push(\n        <View key={i} style={[styles.gridColumn, { left }]}>\n          <View style={styles.gridLine} />\n          {label}\n        </View>\n      );\n    }\n    return cols;\n  }\n}\n\n/* Styles\n============================================================================= */\nconst styles = StyleSheet.create({\n  gridContainer: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0\n  },\n  gridColumn: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    bottom: 0,\n    width: LABELS_WIDTH,\n    backgroundColor: \"transparent\" //'#2d3132',\n    // alignItems: 'center',\n  },\n  gridLine: {\n    position: \"absolute\",\n    left: LABELS_WIDTH / 2,\n    top: 0,\n    bottom: LABELS_HEIGHT,\n    width: 1,\n    backgroundColor: \"rgba(22, 51, 96, 1)\"\n  },\n  gridColumnLabel: {\n    position: \"absolute\",\n    paddingTop: 6,\n    left: 0,\n    right: 0,\n    height: LABELS_HEIGHT,\n    bottom: 0, //-LABELS_HEIGHT,\n    textAlign: \"center\",\n    fontSize: 10,\n    color: \"rgba(95, 118, 162, 1)\",\n    backgroundColor: \"transparent\"\n  }\n});\n"
  },
  {
    "path": "js/tabs/schedule/F8GanttNowMarker.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport moment from \"moment-timezone\";\nimport React from \"react\";\nimport { View, StyleSheet } from \"react-native\";\nimport F8Colors from \"../../common/F8Colors\";\n\n/* constants ================================================================ */\nconst NOW_MARKER_DOT = 6,\n  NOW_MARKER_LINE = 1,\n  LABELS_HEIGHT = 22;\n\n/* =============================================================================\n<F8GanttNowMarker />\n============================================================================= */\n\nexport default class F8GanttNowMarker extends React.Component {\n  render() {\n    const { nowTime, startTime, endTime, containerWidth } = this.props;\n\n    const mNow = moment.utc(nowTime),\n      mDayStart = moment.utc(startTime),\n      mDayEnd = moment.utc(endTime);\n\n    const minutesTotalDayLength = mDayEnd.diff(mDayStart, \"minutes\"),\n      minutesSinceStartOfDay = mNow.diff(mDayStart, \"minutes\");\n\n    const pos =\n      containerWidth / minutesTotalDayLength * minutesSinceStartOfDay -\n      NOW_MARKER_DOT / 2;\n\n    return (\n      <View style={[styles.container, { left: pos }]}>\n        <View style={styles.line} />\n        <View style={styles.dot} />\n      </View>\n    );\n  }\n}\n\n/* Styles\n============================================================================= */\nconst styles = StyleSheet.create({\n  container: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    bottom: LABELS_HEIGHT,\n    width: NOW_MARKER_DOT,\n    backgroundColor: \"transparent\"\n  },\n  line: {\n    position: \"absolute\",\n    width: NOW_MARKER_LINE,\n    left: NOW_MARKER_DOT / 2 - NOW_MARKER_LINE / 2,\n    top: 0,\n    bottom: NOW_MARKER_DOT / 2,\n    backgroundColor: F8Colors.white\n  },\n  dot: {\n    position: \"absolute\",\n    width: NOW_MARKER_DOT,\n    height: NOW_MARKER_DOT,\n    borderRadius: NOW_MARKER_DOT / 2,\n    bottom: 0,\n    left: 0,\n    backgroundColor: F8Colors.white\n  }\n});\n"
  },
  {
    "path": "js/tabs/schedule/F8GanttRow.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport moment from \"moment-timezone\";\nimport React from \"react\";\nimport { View, StyleSheet, Image } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport F8Colors from \"../../common/F8Colors\";\n\n/* constants ================================================================ */\n\nconst ROW_HEIGHT = 26;\n\n/* =============================================================================\n<F8GanttRow />\n============================================================================= */\n\nexport default class F8GanttRow extends React.Component {\n  calculateSize() {\n    const {\n      sessionStart,\n      sessionEnd,\n      dayStart,\n      dayEnd,\n      containerWidth\n    } = this.props;\n\n    const mSessionStart = moment.utc(sessionStart),\n      mSessionEnd = moment.utc(sessionEnd),\n      mDayStart = moment.utc(dayStart),\n      mDayEnd = moment.utc(dayEnd);\n\n    const sessionLength = mSessionEnd.diff(mSessionStart, \"minutes\"),\n      dayLength = mDayEnd.diff(mDayStart, \"minutes\"),\n      daySessionStartDiff = mSessionStart.diff(mDayStart, \"minutes\");\n\n    return {\n      left: daySessionStartDiff / dayLength * containerWidth,\n      width: sessionLength / dayLength * containerWidth\n    };\n  }\n\n  render() {\n    const { location, title, offset } = this.props;\n    const { left, width } = this.calculateSize();\n    let tintColor = location\n      ? F8Colors.colorForLocation(location.toUpperCase())\n      : F8Colors.blue;\n    if (location.toUpperCase().indexOf(\"REGISTRATION\") > -1) {\n      tintColor = F8Colors.yellow;\n    }\n\n    return (\n      <View style={[styles.container, { top: offset, marginLeft: left - 2.5 }]}>\n        <Text numberOfLines={1} style={styles.label}>\n          {title}\n        </Text>\n        <View style={[styles.barContainer, { width: width + 6 }]}>\n          <View style={[styles.bar, { width: width - 8 }]} />\n          <Image\n            style={styles.barStartIcon}\n            source={require(\"./img/gantt-bar-start.png\")}\n          />\n          <Image\n            style={styles.barEndIcon}\n            source={require(\"./img/gantt-bar-end.png\")}\n          />\n          <Image\n            style={[styles.barColorIcon, { tintColor }]}\n            source={require(\"./img/gantt-bar-color.png\")}\n          />\n        </View>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  container: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    height: ROW_HEIGHT,\n    backgroundColor: \"transparent\"\n  },\n  label: {\n    paddingLeft: 2,\n    fontSize: 13,\n    letterSpacing: -0.1,\n    color: F8Colors.white\n  },\n  barContainer: {\n    position: \"absolute\",\n    left: 0,\n    bottom: 0,\n    height: 6\n  },\n  bar: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    top: 2.5,\n    height: 1,\n    overflow: \"hidden\",\n    backgroundColor: F8Colors.colorWithAlpha(\"iceberg\", 0.6)\n  },\n  barStartIcon: {\n    position: \"absolute\",\n    left: 0,\n    bottom: 0\n  },\n  barEndIcon: {\n    position: \"absolute\",\n    right: 9,\n    bottom: 0\n  },\n  barColorIcon: {\n    position: \"absolute\",\n    right: 0,\n    bottom: 0\n  }\n});\n"
  },
  {
    "path": "js/tabs/schedule/F8ScheduleGantt.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport moment from \"moment-timezone\";\nimport React from \"react\";\nimport { View, StyleSheet } from \"react-native\";\n\nimport F8GanttGrid from \"./F8GanttGrid\";\nimport F8GanttNowMarker from \"./F8GanttNowMarker\";\nimport F8GanttRow from \"./F8GanttRow\";\n\n/* constants ================================================================ */\nconst ROW_HEIGHT = 26,\n  ROW_GUTTERS = 10,\n  GRID_PADDING = 10,\n  LABELS_HEIGHT = 22;\n\n/* =============================================================================\n<F8ScheduleGantt />\n--------------------------------------------------------------------------------\nProps:\n  ! sessions:array\n  ! now:number\n============================================================================= */\n\nexport default class F8ScheduleGantt extends React.Component {\n  constructor(props) {\n    super(props);\n\n    const { filtered, earliest, latest } = this.filterSessions(props.sessions);\n    this.state = {\n      now: props.now,\n      filteredSessions: filtered,\n      dayStart: earliest,\n      dayEnd: latest\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    const newState = {};\n    if (nextProps.now !== this.props.now) {\n      newState.now = nextProps.now;\n    }\n    if (nextProps.sessions !== this.props.sessions) {\n      const { filtered, earliest, latest } = this.filterSessions(\n        nextProps.sessions\n      );\n      newState.filteredSessions = filtered;\n      newState.dayStart = earliest;\n      newState.dayEnd = latest;\n    }\n    if (Object.keys(newState).length) {\n      this.setState({ ...newState });\n    }\n  }\n\n  shouldComponentUpdate(nextProps, nextState) {\n    return nextProps !== this.props || nextState !== this.state;\n  }\n\n  filterSessions(all) {\n    let grouped = this.groupSessionsIntoRows(all);\n    let filtered = [];\n    let earliest = null,\n      latest = null;\n\n    (Object.keys(grouped) || []).map(title => {\n      let session = { title, startTime: null, endTime: null, times: [] };\n      (grouped[title] || []).map(each => {\n        // find the earliest and latest start/end times\n        if (!earliest || each.startTime < earliest) {\n          earliest = each.startTime;\n        }\n        if (!latest || each.endTime > latest) {\n          latest = each.endTime;\n        }\n        // update grouped session start/end times (if necessary)\n        if (!session.startTime || each.startTime < session.startTime) {\n          session.startTime = each.startTime;\n        }\n        if (!session.endTime || each.endTime > session.endTime) {\n          session.endTime = each.endTime;\n        }\n        // pass through multiple times, deprecated\n        if (title !== \"Sessions\" && each.times && each.times.length) {\n          session.times = [...session.times, ...each.times];\n        }\n        // set the location (if necessary)\n        if (!session.location) {\n          session.location = each.location;\n        }\n      });\n      filtered.push(session);\n    });\n\n    const roundedDownStart = moment\n        .utc(earliest)\n        .startOf(\"hour\")\n        .valueOf(),\n      endMoment = moment.utc(latest),\n      roundedUpEnd =\n        endMoment.minute() || endMoment.second() || endMoment.millisecond()\n          ? endMoment.add(1, \"hour\").startOf(\"hour\")\n          : endMoment.startOf(\"hour\");\n\n    return {\n      filtered,\n      earliest: roundedDownStart,\n      latest: roundedUpEnd\n    };\n  }\n\n  groupSessionsIntoRows(all) {\n    let grouped = {};\n    (all || []).map(session => {\n      if (session.day !== this.props.day) {\n        return;\n      }\n      if (!session.hasDetails && !grouped[session.title]) {\n        grouped[session.title] = [session]; // start sessions array as its a new entry\n      } else if (!session.hasDetails && grouped[session.title]) {\n        grouped[session.title].push(session); // start sessions array as its a new entry\n      } else if (\n        session.hasDetails &&\n        session.title.indexOf(\"Keynote\") > -1 &&\n        !grouped[session.title]\n      ) {\n        grouped[session.title] = [session];\n      } else if (session.hasDetails && !grouped.Sessions) {\n        grouped.Sessions = [session];\n      } else if (session.hasDetails && grouped.Sessions) {\n        grouped.Sessions.push(session);\n      }\n    });\n    return grouped;\n  }\n\n  render() {\n    const { filteredSessions } = this.state;\n    if (filteredSessions.length < 1) {\n      return null;\n    }\n    const rows = filteredSessions.map((session, index) =>\n      this.renderRow(session, index)\n    );\n    const gutters = rows.length > 1 ? ROW_GUTTERS * (rows.length - 1) : 0;\n    const height =\n      ROW_HEIGHT * rows.length + gutters + GRID_PADDING * 2 + LABELS_HEIGHT;\n    const grid = this.renderGrid(\n      height,\n      this.state.dayStart,\n      this.state.dayEnd\n    );\n    let now;\n    if (\n      this.state.now >= this.state.dayStart &&\n      this.state.now <= this.state.dayEnd\n    ) {\n      now = this.renderNow(\n        this.state.now,\n        this.state.dayStart,\n        this.state.dayEnd\n      );\n    }\n    return (\n      <View style={this.props.style}>\n        <View style={[styles.container, { height }]}>\n          {grid}\n          <View style={{ marginTop: GRID_PADDING }}>{rows}</View>\n          {now}\n        </View>\n      </View>\n    );\n  }\n\n  renderRow(session, i) {\n    const offset = ROW_HEIGHT * i + ROW_GUTTERS * i;\n\n    return (\n      <F8GanttRow\n        containerWidth={this.props.width}\n        dayStart={this.state.dayStart}\n        dayEnd={this.state.dayEnd}\n        sessionStart={session.startTime}\n        sessionEnd={session.endTime}\n        sessionTimes={session.times}\n        location={session.location}\n        title={session.title}\n        offset={offset}\n        key={`GanttRow_${i}`}\n      />\n    );\n  }\n\n  renderGrid(height, start, end) {\n    return (\n      <F8GanttGrid\n        containerWidth={this.props.width}\n        contentHeight={height}\n        startTime={start}\n        endTime={end}\n      />\n    );\n  }\n\n  renderNow(now, start, end) {\n    return (\n      <F8GanttNowMarker\n        containerWidth={this.props.width}\n        nowTime={now}\n        startTime={start}\n        endTime={end}\n      />\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  container: {}\n});\n"
  },
  {
    "path": "js/tabs/schedule/F8SessionCell.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport formatDuration from \"./formatDuration\";\nimport formatTime from \"./formatTime\";\nimport { connect } from \"react-redux\";\nimport type { Session } from \"../../reducers/sessions\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport { Text } from \"../../common/F8Text\";\nimport F8TimelineSegment from \"../../common/F8TimelineSegment\";\nimport { TouchableOpacity, View, Image, StyleSheet } from \"react-native\";\n\n/* Constants\n============================================================================= */\n\nconst CELL_PADDING_TOP = 8,\n  CELL_PADDING_RIGHT = 20,\n  CELL_PADDING_BOTTOM = 12,\n  DURATION_FONT_SIZE = 14,\n  CELL_LEFT = 95,\n  TIMELINE_LEFT = CELL_LEFT - 18,\n  TIMELINE_DOT_TOP = CELL_PADDING_TOP + 7;\n\n/* =============================================================================\n<F8SessionCell />\n============================================================================= */\n\nclass F8SessionCell extends React.Component {\n  props: {\n    session: Session,\n    isFavorite: boolean,\n    showStartEndTime: boolean,\n    onPress: ?() => void,\n    style: any\n  };\n\n  static defaultProps = {\n    firstRow: false,\n    embedded: false\n  };\n\n  render() {\n    const { embedded, isFavorite } = this.props;\n    const embeddedStyles = embedded ? styles.cellEmbedded : null;\n\n    return (\n      <View style={[styles.cell, embeddedStyles, this.props.style]}>\n        {!embedded ? this.renderTimeline() : null}\n        {this.renderContent()}\n        {isFavorite ? this.renderFavoritesIcon() : null}\n      </View>\n    );\n  }\n\n  renderTimeline() {\n    const { firstRow } = this.props;\n    if (firstRow) {\n      return (\n        <F8TimelineSegment\n          left={TIMELINE_LEFT}\n          lineOffsetTop={TIMELINE_DOT_TOP + 2}\n          dotOffsetTop={TIMELINE_DOT_TOP}\n        />\n      );\n    } else {\n      return (\n        <F8TimelineSegment\n          left={TIMELINE_LEFT}\n          dotOffsetTop={TIMELINE_DOT_TOP}\n        />\n      );\n    }\n  }\n\n  renderContent() {\n    if (this.props.onPress) {\n      return (\n        <TouchableOpacity activeOpacity={0.75} onPress={this.props.onPress}>\n          {this.renderTitle()}\n          {this.renderMeta()}\n        </TouchableOpacity>\n      );\n    } else {\n      return [this.renderTitle(), this.renderMeta()];\n    }\n  }\n\n  renderTitle() {\n    const { session } = this.props;\n    const embedded = this.props.embedded ? styles.titleEmbedded : null;\n    return (\n      <View key={`${session.id}_title`} style={styles.titleSection}>\n        <Text numberOfLines={3} style={[styles.titleText, embedded]}>\n          {session.title}\n        </Text>\n      </View>\n    );\n  }\n\n  renderMeta() {\n    const { session } = this.props;\n    return (\n      <Text\n        key={`${session.id}_meta`}\n        numberOfLines={1}\n        style={styles.duration}\n      >\n        <Text style={{ color: F8Colors.colorForLocation(session.location) }}>\n          {session.location.toUpperCase()}\n        </Text>\n        {\" - \"}\n        {this.getFormattedTime()}\n      </Text>\n    );\n  }\n\n  renderFavoritesIcon() {\n    const { title } = this.props.session;\n    let iconSource = require(\"./img/added.png\");\n    if (title && title.toLowerCase().indexOf(\"react\") > -1) {\n      iconSource = require(\"./img/added-react.png\");\n    }\n    return (\n      <View style={styles.added}>\n        <Image style={{ tintColor: F8Colors.pink }} source={iconSource} />\n      </View>\n    );\n  }\n\n  getFormattedTime() {\n    const { startTime, endTime } = this.props.session;\n    if (this.props.showStartEndTime) {\n      return formatTime(startTime, true) + \"-\" + formatTime(endTime);\n    } else {\n      return formatDuration(startTime, endTime);\n    }\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  cell: {\n    paddingTop: CELL_PADDING_TOP,\n    paddingBottom: CELL_PADDING_BOTTOM,\n    paddingLeft: CELL_LEFT,\n    paddingRight: CELL_PADDING_RIGHT,\n    // backgroundColor: F8Colors.background,\n    justifyContent: \"center\"\n  },\n  cellEmbedded: {\n    paddingLeft: CELL_PADDING_RIGHT\n  },\n  titleSection: {\n    paddingRight: 9,\n    flexDirection: \"row\",\n    alignItems: \"center\"\n  },\n  titleAndDuration: {\n    justifyContent: \"center\"\n  },\n  titleText: {\n    flex: 1,\n    fontSize: F8Fonts.normalize(17),\n    lineHeight: 22,\n    color: F8Colors.tangaroa,\n    marginBottom: 3,\n    marginRight: 10\n  },\n  titleEmbedded: {\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\")\n  },\n  duration: {\n    fontSize: DURATION_FONT_SIZE,\n    color: F8Colors.colorWithAlpha(\"tangaroa\", 0.6)\n  },\n  added: {\n    position: \"absolute\",\n    backgroundColor: F8Colors.yellow,\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    width: 23,\n    height: 21,\n    right: 0,\n    top: CELL_PADDING_TOP\n  }\n});\n\n/* Redux\n============================================================================= */\nfunction select(store, props) {\n  return {\n    isFavorite: !!store.schedule[props.session.id]\n  };\n}\n\n/* Export\n============================================================================= */\nmodule.exports = connect(select)(F8SessionCell);\n"
  },
  {
    "path": "js/tabs/schedule/F8SessionDetails.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8FriendGoing from \"./F8FriendGoing\";\nimport F8SpeakerProfile from \"./F8SpeakerProfile\";\nimport MapView from \"../../common/MapView\";\nimport React from \"react\";\nimport AddToScheduleButton from \"./AddToScheduleButton\";\nimport formatDuration from \"./formatDuration\";\nimport { connect } from \"react-redux\";\nimport { addToSchedule, removeFromScheduleWithPrompt } from \"../../actions\";\n\nimport { Dimensions, View, ScrollView, PixelRatio } from \"react-native\";\nimport { Text, Heading2, Heading4, Paragraph } from \"../../common/F8Text\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport ActionsOverlay from \"../../common/ActionsOverlay\";\nimport F8ScrollingHeader from \"../../common/F8ScrollingHeader\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport SharingSettingsModal from \"./SharingSettingsModal\";\nimport LoginModal from \"../../login/LoginModal\";\nimport Carousel from \"../../common/Carousel\";\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  HORIZONTAL_BREAKPOINT = WINDOW_WIDTH <= 320,\n  CONTENT_PADDING_H = HORIZONTAL_BREAKPOINT ? 20 : 30;\n\nconst F8SessionDetails = React.createClass({\n  getInitialState: function() {\n    return {\n      scrollTop: 0,\n      sharingModal: false,\n      loginModal: false\n    };\n  },\n\n  render: function() {\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <ScrollView\n          contentContainerStyle={styles.contentContainer}\n          onScroll={({ nativeEvent }) =>\n            this.setState({ scrollTop: nativeEvent.contentOffset.y })}\n          scrollEventThrottle={100}\n          showsVerticalScrollIndicator={false}\n          automaticallyAdjustContentInsets={false}\n        >\n          {this.renderLocation()}\n          {this.renderTitle()}\n          {this.renderDescription()}\n          {this.renderSpeakers()}\n          {this.renderFriendsGoing()}\n          {this.renderMap()}\n        </ScrollView>\n        {this.renderActions()}\n        {this.renderScrollingHeader()}\n        {this.renderModals()}\n      </View>\n    );\n  },\n\n  renderLocation() {\n    const { session } = this.props;\n    const locationColor = F8Colors.colorForLocation(session.location);\n    const locationTitle = session.location && session.location.toUpperCase();\n    return (\n      <Text style={[styles.location, { color: locationColor }]}>\n        {locationTitle}\n        <Text style={styles.time}>\n          {locationTitle && \" - \"}\n          {formatDuration(session.startTime, session.endTime).toUpperCase()}\n        </Text>\n      </Text>\n    );\n  },\n\n  renderTitle() {\n    if (this.props.session.title) {\n      return (\n        <Heading2 style={styles.title}>{this.props.session.title}</Heading2>\n      );\n    } else {\n      return null;\n    }\n  },\n\n  renderDescription() {\n    if (this.props.session.description) {\n      return <Paragraph>{this.props.session.description}</Paragraph>;\n    } else {\n      return null;\n    }\n  },\n\n  renderSpeakers() {\n    const speakersProfiles = (this.props.session.speakers || []\n    ).map(speaker => (\n      <F8SpeakerProfile\n        key={speaker.name}\n        speaker={speaker}\n        style={{ marginTop: 5 }}\n      />\n    ));\n\n    if (speakersProfiles.length) {\n      return <Section title=\"Hosted By\">{speakersProfiles}</Section>;\n    } else {\n      return null;\n    }\n  },\n\n  renderFriendsGoing() {\n    const friendsGoing = this.props.friendsGoing.map(friend => (\n      <F8FriendGoing\n        key={friend.id}\n        friend={friend}\n        onPress={() => this.props.navigator.push({ friend })}\n      />\n    ));\n    if (friendsGoing.length) {\n      return <Section title=\"Friends Going\">{friendsGoing}</Section>;\n    } else {\n      return null;\n    }\n  },\n\n  renderMap() {\n    if (!this.props.map) {\n      return null;\n    }\n    const mapWidth = Carousel.CardWidth - CONTENT_PADDING_H * 2;\n    return <MapView width={mapWidth} style={styles.map} map={this.props.map} />;\n  },\n\n  renderActions() {\n    const title = this.props.session.title || \"\";\n    const isReactTalk = title.indexOf(\"React\") > -1;\n    return (\n      <ActionsOverlay\n        gradientColors={[\"rgba(255,255,255,0)\", \"rgba(255,255,255,1)\"]}\n        buttonContainerStyles={{ paddingHorizontal: 15, paddingBottom: 12 }}\n        style={styles.actions}\n      >\n        <AddToScheduleButton\n          style={{ flex: 1 }}\n          addedImageSource={\n            isReactTalk ? require(\"./img/added-react.png\") : null\n          }\n          isAdded={this.props.isAddedToSchedule}\n          onPress={this.toggleAdded}\n        />\n      </ActionsOverlay>\n    );\n  },\n\n  renderScrollingHeader() {\n    const { title } = this.props.session;\n    return (\n      <F8ScrollingHeader\n        contentInset={CONTENT_PADDING_H}\n        scrollTop={this.state.scrollTop}\n        text={title}\n      />\n    );\n  },\n\n  renderModals() {\n    return [\n      <SharingSettingsModal\n        key=\"modal_sharingsettings\"\n        navigator={this.props.navigator}\n        visible={this.state.sharingModal}\n        animationType=\"fade\"\n        transparent={true}\n        onSetSharing={_ => {\n          this.setState({ sharingModal: false });\n        }}\n      />,\n      <LoginModal\n        key=\"modal_login\"\n        visible={this.state.loginModal}\n        animationType=\"fade\"\n        transparent={true}\n        navigator={this.props.navigator}\n        onLogin={this.addToSchedule}\n        onClose={_ => this.setState({ loginModal: false })}\n      />\n    ];\n  },\n\n  toggleAdded: function() {\n    if (this.props.isAddedToSchedule) {\n      this.props.removeFromScheduleWithPrompt();\n    } else {\n      this.addToSchedule();\n    }\n  },\n\n  addToSchedule: function() {\n    if (!this.props.isLoggedIn) {\n      // this.props.navigator.push({\n      //   login: true, // TODO: Proper route\n      //   callback: this.addToSchedule,\n      // });\n      this.setState({ loginModal: true });\n    } else {\n      this.props.addToSchedule();\n      if (this.props.sharedSchedule === null) {\n        setTimeout(_ => this.setState({ sharingModal: true }), 1000);\n        // setTimeout(() => this.props.navigator.push({share: true}), 1000);\n      }\n    }\n  }\n});\n\nclass Section extends React.Component {\n  props: {\n    title?: string,\n    children?: any\n  };\n\n  render() {\n    const { children } = this.props;\n    if (React.Children.count(children) === 0) {\n      return null;\n    }\n    let header;\n    if (this.props.title) {\n      header = (\n        <Heading4 style={styles.sectionTitle}>\n          {this.props.title.toUpperCase()}\n        </Heading4>\n      );\n    }\n    return (\n      <View style={styles.section}>\n        {header}\n        {children}\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: \"white\"\n  },\n  contentContainer: {\n    paddingTop: 23,\n    paddingHorizontal: CONTENT_PADDING_H,\n    paddingBottom: 93\n  },\n  miniHeader: {\n    backgroundColor: F8Colors.white,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    position: \"absolute\",\n    left: 24,\n    top: 0,\n    right: 24,\n    paddingVertical: 9,\n    borderBottomWidth: 1 / PixelRatio.get(),\n    borderBottomColor: \"rgba(153, 162, 178, 1)\"\n  },\n  miniTitle: {\n    fontSize: 13,\n    color: F8Colors.tangaroa,\n    ios: {\n      textAlign: \"left\"\n    }\n  },\n  location: {\n    fontSize: 13,\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helveticaBold\")\n  },\n  time: {\n    color: F8Colors.tangaroa,\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helveticaBold\")\n  },\n  title: {\n    marginVertical: 15,\n    color: F8Colors.blue\n  },\n  section: {\n    marginTop: 20\n  },\n  sectionTitle: {\n    marginBottom: 6\n  },\n  actions: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0\n  },\n  map: {\n    marginTop: 32\n  }\n});\n\nfunction select(store, props) {\n  const sessionID = props.session.id;\n  const friendsGoing = store.friendsSchedules.filter(\n    friend => friend.schedule && friend.schedule[sessionID]\n  );\n  const map = store.maps.find(({ name }) => name === props.session.location);\n\n  return {\n    isAddedToSchedule: !!store.schedule[props.session.id],\n    isLoggedIn: store.user.isLoggedIn,\n    sharedSchedule: store.user.sharedSchedule,\n    sessionURLTemplate: store.config.sessionURLTemplate,\n    topics: store.topics,\n    friendsGoing,\n    map\n  };\n}\n\nfunction actions(dispatch, props) {\n  let id = props.session.id;\n  return {\n    addToSchedule: () => dispatch(addToSchedule(id)),\n    removeFromScheduleWithPrompt: () =>\n      dispatch(removeFromScheduleWithPrompt(props.session))\n  };\n}\n\nmodule.exports = connect(select, actions)(F8SessionDetails);\n"
  },
  {
    "path": "js/tabs/schedule/F8SpeakerProfile.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport React from \"react\";\nimport { Heading3, Text } from \"../../common/F8Text\";\nimport { StyleSheet, View } from \"react-native\";\n\nconst F8SpeakerProfile = React.createClass({\n  render: function() {\n    const speaker = this.props.speaker;\n    return (\n      <View style={[styles.row, this.props.style]}>\n        <Heading3 style={styles.name}>{speaker.name}</Heading3>\n        {speaker.title ? (\n          <Text style={styles.title}>{speaker.title}</Text>\n        ) : null}\n      </View>\n    );\n  }\n});\n\nconst styles = StyleSheet.create({\n  row: {\n    paddingBottom: 14\n  },\n  name: {\n    color: F8Colors.blue\n  },\n  title: {\n    fontSize: 13,\n    lineHeight: 16,\n    color: F8Colors.tangaroa\n  }\n});\n\nmodule.exports = F8SpeakerProfile;\n"
  },
  {
    "path": "js/tabs/schedule/FilterHeader.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { View, Image, TouchableOpacity } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Colors from \"../../common/F8Colors\";\n\n/* constants ================================================================ */\n\nconst CONTAINER_HEIGHT = 39;\n\n/* <FilterHeader />\n============================================================================= */\n\nclass FilterHeader extends React.Component {\n  static defaultProps = {\n    backgroundColor: F8Colors.tangaroa,\n    textColor: F8Colors.white\n  };\n\n  render() {\n    const topics = Object.keys(this.props.filter);\n    if (topics.length === 0) {\n      return null;\n    }\n\n    const { backgroundColor, textColor } = this.props;\n\n    return (\n      <View style={[styles.container, { backgroundColor }, this.props.style]}>\n        <Text style={[styles.text, { color: textColor }]} numberOfLines={1}>\n          {`Filter${topics.length > 1 ? \"s\" : \"\"}: ${topics.join(\", \")}`}\n        </Text>\n        <TouchableOpacity\n          accessibilityLabel=\"Clear filter\"\n          accessibilityTraits=\"button\"\n          style={styles.clear}\n          onPress={this.props.onClear}\n        >\n          <Image source={require(\"./img/filters-x.png\")} />\n        </TouchableOpacity>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    height: CONTAINER_HEIGHT,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n\n    ios: {\n      paddingLeft: 37\n    },\n    android: {\n      paddingLeft: 12\n    }\n  },\n  text: {\n    flex: 1,\n    fontSize: 12,\n\n    ios: {\n      textAlign: \"center\"\n    }\n  },\n  clear: {\n    paddingHorizontal: 12,\n    alignSelf: \"stretch\",\n    justifyContent: \"center\"\n  }\n});\n\n/* exports ================================================================== */\nmodule.exports = FilterHeader;\n"
  },
  {
    "path": "js/tabs/schedule/FriendCell.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { View, Image, StyleSheet, TouchableHighlight } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport ProfilePicture from \"../../common/ProfilePicture\";\n\nimport type { FriendsSchedule } from \"../../reducers/friendsSchedules\";\n\nclass FriendCell extends React.Component {\n  props: {\n    friend: FriendsSchedule,\n    onPress: ?() => void\n  };\n\n  render() {\n    const { friend } = this.props;\n    const hasSchedule =\n      friend.schedule && Object.keys(friend.schedule).length > 0;\n    const auxView = hasSchedule ? (\n      <Image source={require(\"../../common/img/pointer-right.png\")} />\n    ) : (\n      <Image\n        style={{ tintColor: F8Colors.blueBayoux, width: 12, height: 15 }}\n        source={require(\"../../common/img/privacy.png\")}\n      />\n    );\n\n    const cell = (\n      <View style={styles.cell}>\n        <ProfilePicture userID={friend.id} size={48} />\n        <Text style={styles.name}>{friend.name}</Text>\n        {auxView}\n      </View>\n    );\n\n    if (!hasSchedule) {\n      return cell;\n    } else {\n      return (\n        <TouchableHighlight underlayColor=\"white\" onPress={this.props.onPress}>\n          {cell}\n        </TouchableHighlight>\n      );\n    }\n  }\n}\n\nconst styles = StyleSheet.create({\n  cell: {\n    height: 68,\n    paddingLeft: 11,\n    paddingRight: 15,\n    flexDirection: \"row\",\n    backgroundColor: F8Colors.bianca,\n    alignItems: \"center\"\n  },\n  name: {\n    flex: 1,\n    fontSize: 17,\n    marginHorizontal: 13,\n    color: F8Colors.tangaroa\n  },\n  private: {\n    color: F8Colors.lightText\n  }\n});\n\nmodule.exports = FriendCell;\n"
  },
  {
    "path": "js/tabs/schedule/FriendsListView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport EmptySchedule from \"./EmptySchedule\";\n\nimport React from \"react\";\n\nimport InviteFriendsButton from \"./InviteFriendsButton\";\nimport PureListView from \"../../common/PureListView\";\nimport FriendCell from \"./FriendCell\";\n\nimport { View } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport LoginButton from \"../../common/LoginButton\";\n\ntype Friend = any;\n\ntype Props = {\n  friends: Array<Friend>,\n  navigator: Navigator\n};\n\nclass FriendsListView extends React.Component {\n  props: Props;\n  _innerRef: ?PureListView;\n\n  constructor(props: Props) {\n    super(props);\n\n    this._innerRef = null;\n\n    (this: any).renderRow = this.renderRow.bind(this);\n    (this: any).renderFooter = this.renderFooter.bind(this);\n    (this: any).renderEmptyList = this.renderEmptyList.bind(this);\n    (this: any).storeInnerRef = this.storeInnerRef.bind(this);\n  }\n\n  render() {\n    return (\n      <PureListView\n        ref={this.storeInnerRef}\n        data={this.props.friends}\n        renderRow={this.renderRow}\n        renderEmptyList={this.renderEmptyList}\n        renderFooter={this.renderFooter}\n        {...(this.props: any) /* flow can't guarantee the shape of props */}\n      />\n    );\n  }\n\n  renderRow(friend: Friend) {\n    return (\n      <FriendCell\n        friend={friend}\n        onPress={() => this.openFriendsSchedule(friend)}\n      />\n    );\n  }\n\n  renderEmptyList(containerHeight: number): ?ReactElement {\n    if (containerHeight === 0) {\n      return null;\n    }\n    if (!this.props.loggedIn) {\n      return (\n        <EmptySchedule\n          style={{ height: containerHeight }}\n          key=\"login\"\n          title=\"Log in to see your friends at F8.\"\n          text=\"You’ll be able to view each other’s custom schedules.\"\n        >\n          <LoginButton source=\"My F8\" />\n        </EmptySchedule>\n      );\n    }\n    return (\n      <EmptySchedule\n        style={{ height: containerHeight }}\n        image={require(\"./img/no-friends-found.png\")}\n        text={\"Friends using the F8 app\\nwill appear here.\"}\n      >\n        <InviteFriendsButton />\n      </EmptySchedule>\n    );\n  }\n\n  renderFooter() {\n    return (\n      <View style={{ paddingHorizontal: 20, paddingVertical: 15 }}>\n        <InviteFriendsButton />\n      </View>\n    );\n  }\n\n  openFriendsSchedule(friend: Friend) {\n    this.props.navigator.push({ friend });\n  }\n\n  storeInnerRef(ref: ?PureListView) {\n    this._innerRef = ref;\n  }\n\n  scrollTo(...args: Array<any>) {\n    this._innerRef && this._innerRef.scrollTo(...args);\n  }\n\n  getScrollResponder(): any {\n    return this._innerRef && this._innerRef.getScrollResponder();\n  }\n}\n\nmodule.exports = FriendsListView;\n"
  },
  {
    "path": "js/tabs/schedule/FriendsScheduleView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport EmptySchedule from \"./EmptySchedule\";\nimport FilterSessions from \"./filterSessions\";\nimport ListContainer from \"../../common/ListContainer\";\nimport ScheduleListView from \"./ScheduleListView\";\n\nimport { View, StatusBar } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport MessengerChatHead from \"../../common/MessengerChatHead\";\n\nimport { connect } from \"react-redux\";\n\nimport type { Session } from \"../../reducers/sessions\";\nimport type { FriendsSchedule } from \"../../reducers/friendsSchedules\";\n\nimport F8TimelineBackground from \"../../common/F8TimelineBackground\";\nimport F8Colors from \"../../common/F8Colors\";\n\nimport { createSelector } from \"reselect\";\n\ntype Props = {\n  sessions: Array<Session>,\n  friend: FriendsSchedule,\n  navigator: Navigator\n};\n\nclass FriendsScheduleView extends React.Component {\n  props: Props;\n\n  constructor(props) {\n    super(props);\n    (this: any).renderEmptyList = this.renderEmptyList.bind(this);\n  }\n\n  render() {\n    const backItem = {\n      title: \"Back\",\n      layout: \"icon\",\n      icon: require(\"../../common/img/header/back-blue.png\"),\n      onPress: () => this.props.navigator.pop()\n    };\n    const firstName = this.props.friend.name.split(\" \")[0];\n    return (\n      <View style={{ flex: 1 }}>\n        <StatusBar barStyle=\"dark-content\" animated={true} />\n        <ListContainer\n          title={`${firstName}'s Schedule`}\n          headerBackgroundColor={F8Colors.bianca}\n          headerTitleColor={F8Colors.blue}\n          headerItemsColor={F8Colors.blue}\n          segmentedBorderColor={F8Colors.blue}\n          segmentedTextColor={F8Colors.sapphire2}\n          navItem={backItem}\n        >\n          <ScheduleListView\n            title=\"Day 1\"\n            day={1}\n            sessions={this.props.sessions}\n            renderEmptyList={this.renderEmptyList}\n            renderFooter={_ => <F8TimelineBackground />}\n            navigator={this.props.navigator}\n          />\n          <ScheduleListView\n            title=\"Day 2\"\n            day={2}\n            sessions={this.props.sessions}\n            renderEmptyList={this.renderEmptyList}\n            renderFooter={_ => <F8TimelineBackground />}\n            navigator={this.props.navigator}\n          />\n        </ListContainer>\n        <MessengerChatHead\n          user={this.props.friend}\n          style={{\n            position: \"absolute\",\n            right: 12,\n            bottom: 18\n          }}\n        />\n      </View>\n    );\n  }\n\n  renderEmptyList(day, containerHeight) {\n    return (\n      <EmptySchedule\n        style={{ height: containerHeight }}\n        title=\"Nothing to show.\"\n        text={`${this.props.friend\n          .name} has not added any sessions for day ${day}`}\n      />\n    );\n  }\n}\n\nconst data = createSelector(\n  store => store.sessions,\n  (store, props) => props.friend.schedule,\n  (sessions, schedule) => FilterSessions.bySchedule(sessions, schedule)\n);\n\nfunction select(store, props) {\n  return {\n    sessions: data(store, props)\n  };\n}\n\nmodule.exports = connect(select)(FriendsScheduleView);\n"
  },
  {
    "path": "js/tabs/schedule/FriendsUsingApp.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { connect } from \"react-redux\";\nimport { View, Image, StyleSheet } from \"react-native\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Text } from \"../../common/F8Text\";\n\nconst PROFILE_PICTURE_SIZE = 30;\n\nclass FriendsUsingApp extends React.Component {\n  props: {\n    friends: Array<{ id: string, name: string }>\n  };\n\n  static defaultProps = {\n    multiline: false,\n    photoBorderColor: F8Colors.bianca,\n    textColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.6)\n  };\n\n  render() {\n    const { friends } = this.props;\n    if (friends.length === 0) {\n      return null;\n    } else {\n      return this.props.multiline\n        ? this.renderMultiline()\n        : this.renderSingleRow();\n    }\n  }\n\n  renderSingleRow() {\n    const { friends, photoBorderColor, textColor } = this.props;\n    const pictures = friends\n      .slice(0, 2)\n      .map(friend => (\n        <Image\n          key={friend.id}\n          source={{ uri: `https://graph.facebook.com/${friend.id}/picture` }}\n          style={[\n            styles.profilePic,\n            { borderColor: photoBorderColor, marginRight: -7, borderWidth: 1 }\n          ]}\n        />\n      ));\n    let text = `${friends.length} friends are using the F8 app.`;\n    if (friends.length === 1) {\n      text = `${friends[0].name.split(\" \")[0]} is using the F8 app.`;\n    }\n    return (\n      <View\n        style={[styles.container, { flexDirection: \"row\" }, this.props.style]}\n      >\n        <View style={{ flexDirection: \"row-reverse\" }}>{pictures}</View>\n        <Text style={[styles.text, { color: textColor, marginLeft: 21 }]}>\n          {text}\n        </Text>\n      </View>\n    );\n  }\n\n  renderMultiline() {\n    const { friends, textColor } = this.props;\n    let names = [];\n    let pictures = [];\n    const showFriendsDetailsCount = 2;\n    friends.slice(0, showFriendsDetailsCount).map((friend, idx) => {\n      names.push(friend.name);\n      pictures.push(\n        <Image\n          key={friend.id}\n          source={{ uri: `https://graph.facebook.com/${friend.id}/picture` }}\n          style={[styles.profilePic, { marginHorizontal: 1 }]}\n        />\n      );\n    });\n    const text = this.getLabelWithFriendNames(names, friends);\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <View style={{ marginBottom: 18, flexDirection: \"row\" }}>\n          {pictures}\n        </View>\n        <Text style={[styles.text, { color: textColor, textAlign: \"center\" }]}>\n          {text}\n        </Text>\n      </View>\n    );\n  }\n\n  getLabelWithFriendNames(names = [], friends = []) {\n    if (friends.length === 1) {\n      return `${friends[0].name} is using the F8 app.`;\n    }\n    if (names.length - friends.length === 0) {\n      return `${friends.length} friends are using the F8 app.`;\n    }\n    const othersNumber = friends.length - names.length;\n    const pluralizeOthers =\n      othersNumber > 1\n        ? `${othersNumber} others are`\n        : `${othersNumber} other friend are`;\n    return `${names.join(\", \")} and ${pluralizeOthers} using the F8 app.`;\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: \"center\"\n  },\n  profilePic: {\n    width: PROFILE_PICTURE_SIZE,\n    height: PROFILE_PICTURE_SIZE,\n    borderRadius: PROFILE_PICTURE_SIZE / 2\n  },\n  text: {\n    fontSize: 13\n  }\n});\n\nfunction select(store) {\n  return {\n    friends: store.friendsSchedules\n  };\n}\n\nmodule.exports = connect(select)(FriendsUsingApp);\n"
  },
  {
    "path": "js/tabs/schedule/GeneralScheduleView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport EmptySchedule from \"./EmptySchedule\";\nimport FilterHeader from \"./FilterHeader\";\nimport FilterSessions from \"./filterSessions\";\nimport ListContainer from \"../../common/ListContainer\";\nimport React from \"react\";\nimport ScheduleListView from \"./ScheduleListView\";\nimport FilterScreen from \"../../filter/FilterScreen\";\nimport { connect } from \"react-redux\";\nimport {\n  switchDay,\n  applyScheduleFilter,\n  clearScheduleFilter\n} from \"../../actions\";\n\nimport type { Session } from \"../../reducers/sessions\";\nimport { Dimensions } from \"react-native\";\nimport { Platform, View } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport F8TimelineBackground from \"../../common/F8TimelineBackground\";\nimport HideCompleted from \"./HideCompleted\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport F8ScheduleGantt from \"./F8ScheduleGantt\";\nimport { sessionsHappeningToday } from \"../../common/convertTimes\";\n\n// TODO: Move from reselect to memoize?\nimport { createSelector } from \"reselect\";\n\nconst data = createSelector(\n  store => store.sessions,\n  store => store.scheduleFilter,\n  (sessions, filter) => FilterSessions.byTopics(sessions, filter)\n);\n\nconst GANTT_PADDDING_H = 14,\n  GANTT_WIDTH = Dimensions.get(\"window\").width - GANTT_PADDDING_H * 2;\n\ntype Props = {\n  filter: any,\n  day: number,\n  sessions: Array<Session>,\n  navigator: Navigator,\n  logOut: () => void,\n  switchDay: (day: number) => void\n};\n\nclass GeneralScheduleView extends React.Component {\n  props: Props;\n\n  constructor(props) {\n    super(props);\n\n    (this: any).renderEmptyList = this.renderEmptyList.bind(this);\n    (this: any).switchDay = this.switchDay.bind(this);\n    (this: any).openFilterScreen = this.openFilterScreen.bind(this);\n    (this: any).renderStickyHeader = this.renderStickyHeader.bind(this);\n\n    this.state = {\n      hideCompleted: false,\n      filterModal: false,\n      sessionsHappeningToday: sessionsHappeningToday(props.now),\n      incompleteSessions: FilterSessions.byCompleted(props.sessions, props.now)\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (\n      nextProps.sessions !== this.props.sessions ||\n      nextProps.now !== this.props.now\n    ) {\n      this.setState({\n        sessionsHappeningToday: sessionsHappeningToday(nextProps.now),\n        incompleteSessions: FilterSessions.byCompleted(\n          nextProps.sessions,\n          nextProps.now\n        )\n      });\n    }\n  }\n\n  render() {\n    let sessions = [...this.props.sessions];\n    if (this.state.hideCompleted && this.state.sessionsHappeningToday) {\n      sessions = [...this.state.incompleteSessions];\n    }\n\n    const content = (\n      <ListContainer\n        title=\"Schedule\"\n        selectedSegment={this.props.day - 1}\n        onSegmentChange={this.switchDay}\n        stickyHeader={this.renderStickyHeader()}\n        leftItem={{\n          title: \"Map\",\n          layout: \"icon\",\n          icon: require(\"../../common/img/header/map.png\"),\n          onPress: _ =>\n            this.props.navigator && this.props.navigator.push({ maps: true })\n        }}\n        rightItem={{\n          icon: require(\"../../common/img/header/filter.png\"),\n          title: \"Filter\",\n          onPress: this.openFilterScreen\n        }}\n      >\n        <ScheduleListView\n          title=\"Day 1\"\n          day={1}\n          sessions={sessions}\n          renderEmptyList={this.renderEmptyList}\n          renderHeader={_ => this.renderGanttChart(1, sessions)}\n          renderFooter={_ => <F8TimelineBackground height={80} />}\n          navigator={this.props.navigator}\n        />\n        <ScheduleListView\n          title=\"Day 2\"\n          day={2}\n          sessions={sessions}\n          renderEmptyList={this.renderEmptyList}\n          renderHeader={_ => this.renderGanttChart(2, sessions)}\n          renderFooter={_ => <F8TimelineBackground height={80} />}\n          navigator={this.props.navigator}\n        />\n      </ListContainer>\n    );\n\n    if (Platform.OS === \"ios\") {\n      return content;\n    } else {\n      return (\n        <View style={{ flex: 1 }}>\n          {content}\n          <FilterScreen\n            visible={this.state.filterModal}\n            topics={this.props.topics}\n            selectedTopics={this.props.filter}\n            onClose={_ => this.setState({ filterModal: false })}\n            onApply={selected => this.props.filterTopics(selected)}\n          />\n        </View>\n      );\n    }\n  }\n\n  renderStickyHeader() {\n    let hideCompletedBar;\n    if (this.state.sessionsHappeningToday) {\n      hideCompletedBar = (\n        <HideCompleted\n          enabled={this.state.hideCompleted}\n          onChange={hideCompleted => this.setState({ hideCompleted })}\n        />\n      );\n    }\n    let filterHeader;\n    if (Object.keys(this.props.filter).length > 0) {\n      filterHeader = (\n        <FilterHeader\n          filter={this.props.filter}\n          onClear={_ => this.props.clearFilter()}\n        />\n      );\n    }\n\n    return (\n      <View>\n        {hideCompletedBar}\n        {filterHeader}\n      </View>\n    );\n  }\n\n  renderGanttChart(day: number, sessions: Array<Session>) {\n    const hasFilters = Object.keys(this.props.filter).length > 0;\n\n    if (hasFilters || !sessions.length) {\n      // intercept when list is filtered\n      // also disallow empty sessions to prevent the overflow color view from rendering\n      return <View style={{ height: 15 }} />; // TODO: better solution than spacer view\n    }\n    return (\n      <View style={{ paddingBottom: 15 }}>\n        <View\n          style={{\n            position: \"absolute\",\n            left: 0,\n            right: 0,\n            bottom: 15,\n            height: 1000,\n            backgroundColor: F8Colors.tangaroa\n          }}\n        />\n        <F8ScheduleGantt\n          style={{\n            backgroundColor: F8Colors.tangaroa,\n            paddingTop: 18,\n            paddingBottom: 12,\n            paddingHorizontal: GANTT_PADDDING_H\n          }}\n          sessions={sessions}\n          day={day}\n          width={GANTT_WIDTH}\n          now={this.props.now}\n        />\n      </View>\n    );\n  }\n\n  renderEmptyList(day: number, containerHeight: number) {\n    const otherDay = day === 1 ? 2 : 1;\n    const dayDir = day === 1 ? \"left\" : \"right\";\n    return (\n      <EmptySchedule\n        style={{ height: containerHeight }}\n        title={`No Day ${day} matches`}\n        titleStyles={{ marginBottom: 5 }}\n        text={`Swipe ${dayDir} for Day ${otherDay}`.toUpperCase()}\n        textStyles={{\n          fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helvetica\"),\n          color: F8Colors.colorWithAlpha(\"tangaroa\", 0.5),\n          fontSize: 13\n        }}\n      />\n    );\n  }\n\n  openFilterScreen() {\n    if (Platform.OS === \"ios\") {\n      this.props.navigator.push({\n        filter: true,\n        topics: this.props.topics,\n        selectedTopics: this.props.filter,\n        onApply: selected => this.props.filterTopics(selected)\n      });\n    } else {\n      this.setState({ filterModal: true });\n    }\n  }\n\n  switchDay(page: number) {\n    this.props.switchDay(page + 1);\n  }\n}\n\nfunction select(store) {\n  return {\n    day: store.navigation.day,\n    filter: store.scheduleFilter,\n    topics: store.scheduleTopics,\n    sessions: data(store)\n  };\n}\n\nfunction actions(dispatch) {\n  return {\n    switchDay: day => dispatch(switchDay(day)),\n    filterTopics: selected => dispatch(applyScheduleFilter(selected)),\n    clearFilter: _ => dispatch(clearScheduleFilter())\n  };\n}\n\nmodule.exports = connect(select, actions)(GeneralScheduleView);\n"
  },
  {
    "path": "js/tabs/schedule/HideCompleted.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport { Platform, StyleSheet, View, Switch, Alert } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\n\nconst CONTAINER_HEIGHT = 46;\n\n/* =============================================================================\n<HideCompleted />\n============================================================================= */\n\nclass HideCompleted extends React.Component {\n  static defaultProps = {\n    enabled: false,\n    label: \"Hide completed\",\n    backgroundColor: F8Colors.persianBlue,\n    textColor: F8Colors.white,\n    switchInactiveColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.8),\n    switchActiveColor: F8Colors.blue,\n    onChange: _ => {}\n  };\n\n  render() {\n    const { label, backgroundColor, textColor } = this.props;\n    return (\n      <View style={[styles.container, { backgroundColor }, this.props.style]}>\n        <Text numberOfLines={1} style={[styles.text, { color: textColor }]}>\n          {label.toUpperCase()}\n        </Text>\n        {this.renderSwitch()}\n      </View>\n    );\n  }\n\n  renderSwitch() {\n    const { enabled, onChange, switchInactiveColor } = this.props;\n    if (Platform.OS === \"ios\") {\n      return (\n        <View style={styles.toggleSwitch}>\n          <View\n            style={[\n              styles.toggleBackground,\n              { backgroundColor: switchInactiveColor }\n            ]}\n          />\n          <Switch\n            value={enabled}\n            tintColor={switchInactiveColor}\n            onValueChange={onChange}\n          />\n        </View>\n      );\n    } else {\n      return (\n        <Switch\n          value={enabled}\n          tintColor={switchInactiveColor}\n          onValueChange={onChange}\n          style={styles.toggleSwitch}\n        />\n      );\n    }\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    height: CONTAINER_HEIGHT,\n    paddingHorizontal: 18,\n    flexDirection: \"row\",\n    alignItems: \"center\"\n  },\n  text: {\n    flex: 1,\n    fontSize: 13,\n    fontFamily: F8Fonts.helvetica\n  },\n  toggleSwitch: {\n    flex: 0\n  },\n  toggleBackground: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    borderRadius: 20\n  }\n});\n\n/* playground cards ========================================================= */\n\nconst hideCompleted = HideCompleted;\nhideCompleted.__cards__ = define => {\n  define(\"Default\", _ => <HideCompleted />);\n  define(\"Customized\", _ => (\n    <HideCompleted\n      onChange={val => Alert.alert(val)}\n      label=\"Customized Example\"\n      enabled={false}\n      backgroundColor={F8Colors.turquoise}\n      textColor={F8Colors.tangaroa}\n      switchInactiveColor={F8Colors.pink}\n      switchActiveColor={F8Colors.yellow}\n    />\n  ));\n};\n\n/* exports ================================================================== */\nmodule.exports = hideCompleted;\n"
  },
  {
    "path": "js/tabs/schedule/InviteFriendsButton.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Button from \"../../common/F8Button\";\nimport { AppInviteDialog } from \"react-native-fbsdk\";\nimport { connect } from \"react-redux\";\nimport F8Analytics from \"../../F8Analytics\";\nimport { Alert } from \"react-native\";\n\nclass InviteFriendsButton extends React.Component {\n  props: {\n    appLinkURL: string,\n    appInvitePreviewImageURL: string,\n    style: any\n  };\n\n  render() {\n    if (!this.props.appLinkURL) {\n      return null;\n    }\n\n    return (\n      <F8Button\n        theme=\"bordered\"\n        fontSize={13}\n        opacity={0.6}\n        caption=\"Invite friends to the F8 app\"\n        onPress={() => this.inviteFriends()}\n      />\n    );\n  }\n\n  inviteFriends() {\n    F8Analytics.logEvent(\"Invite Friends\", 1);\n    AppInviteDialog.show({\n      applinkUrl: this.props.appLinkURL,\n      previewImageUrl: this.props.appInvitePreviewImageURL\n    }).catch(error => {\n      if (error.message) {\n        Alert.alert(error.message);\n      }\n    });\n  }\n}\n\nfunction select(store) {\n  return {\n    appLinkURL: store.config.appLinkURL,\n    appInvitePreviewImageURL: store.config.appInvitePreviewImageURL\n  };\n}\n\nmodule.exports = connect(select)(InviteFriendsButton);\n"
  },
  {
    "path": "js/tabs/schedule/MyScheduleView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport EmptySchedule from \"./EmptySchedule\";\nimport F8Button from \"../../common/F8Button\";\nimport FilterSessions from \"./filterSessions\";\nimport ListContainer from \"../../common/ListContainer\";\nimport LoginButton from \"../../common/LoginButton\";\nimport React from \"react\";\nimport ScheduleListView from \"./ScheduleListView\";\nimport FriendsListView from \"./FriendsListView\";\nimport { connect } from \"react-redux\";\nimport { View, StatusBar } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport F8Colors from \"../../common/F8Colors\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8TimelineBackground from \"../../common/F8TimelineBackground\";\n\nimport { HeaderTitle } from \"../../common/F8Text\";\nimport PrivacyIcon from \"./PrivacyIcon\";\nimport HideCompleted from \"./HideCompleted\";\nimport F8Tooltip from \"../../common/F8Tooltip\";\n\nimport { sessionsHappeningToday } from \"../../common/convertTimes\";\n\nimport {\n  logOutWithPrompt,\n  switchTab,\n  switchDay,\n  loadFriendsSchedules\n} from \"../../actions\";\n\nimport type { Session } from \"../../reducers/sessions\";\nimport type { FriendsSchedule } from \"../../reducers/friendsSchedules\";\nimport type { State as User } from \"../../reducers/user\";\nimport type { State as Schedule } from \"../../reducers/schedule\";\n\nimport { createSelector } from \"reselect\";\n\nconst PRIVACY_ICON_SIZE = 20;\n\ntype Props = {\n  user: User,\n  sessions: Array<Session>,\n  friends: Array<FriendsSchedule>,\n  schedule: Schedule,\n  navigator: Navigator,\n  logOut: () => void,\n  jumpToSchedule: (day: number) => void,\n  loadFriends: () => void\n};\n\n// TODO: Rename to MyF8View\nclass MyScheduleView extends React.Component {\n  props: Props;\n\n  constructor(props) {\n    super(props);\n\n    (this: any).renderEmptySessionsList = this.renderEmptySessionsList.bind(\n      this\n    );\n    (this: any).openSharingSettings = this.openSharingSettings.bind(this);\n    (this: any).handleSegmentChanged = this.handleSegmentChanged.bind(this);\n    (this: any).dismiss = this.dismiss.bind(this);\n    (this: any).renderStickyHeader = this.renderStickyHeader.bind(this);\n\n    this.state = {\n      hideCompleted: false,\n      privacyModal: false,\n      showStickyHeader: true,\n      tooltipX: 0,\n      tooltipY: 0,\n      sessionsHappeningToday: sessionsHappeningToday(props.now),\n      incompleteSessions: FilterSessions.byCompleted(props.sessions, props.now)\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (\n      nextProps.sessions !== this.props.sessions ||\n      nextProps.now !== this.props.now\n    ) {\n      this.setState({\n        sessionsHappeningToday: sessionsHappeningToday(nextProps.now),\n        incompleteSessions: FilterSessions.byCompleted(\n          nextProps.sessions,\n          nextProps.now\n        )\n      });\n    }\n  }\n\n  render() {\n    let rightItem;\n    if (this.props.user.isLoggedIn) {\n      rightItem = {\n        title: \"Settings\",\n        layout: \"icon\",\n        icon: require(\"../../common/img/header/settings.png\"),\n        onPress: this.openSharingSettings\n      };\n    }\n\n    return (\n      <View style={{ flex: 1 }}>\n        <StatusBar barStyle=\"light-content\" animated={true} />\n        <ListContainer\n          headerContent={this.renderHeaderContent()}\n          onSegmentChange={this.handleSegmentChanged}\n          leftItem={{\n            title: \"Map\",\n            layout: \"icon\",\n            icon: require(\"../../common/img/header/map.png\"),\n            onPress: _ =>\n              this.props.navigator && this.props.navigator.push({ maps: true })\n          }}\n          rightItem={rightItem}\n          headerBackgroundColor={F8Colors.pink}\n          headerTitleColor={F8Colors.yellow}\n          headerItemsColor={F8Colors.white}\n          segmentedTextColor={F8Colors.white}\n          segmentedBorderColor={F8Colors.yellow}\n          stickyHeader={this.renderStickyHeader()}\n        >\n          {this.renderContent()}\n        </ListContainer>\n        {this.renderPrivacyModal()}\n      </View>\n    );\n  }\n\n  renderContent() {\n    let sessions = [...this.props.sessions];\n    if (this.state.hideCompleted && this.state.sessionsHappeningToday) {\n      sessions = [...this.state.incompleteSessions];\n    }\n\n    return [\n      <ScheduleListView\n        key={\"MSV_SLV_d1\"}\n        title=\"Day 1\"\n        day={1}\n        sessions={sessions}\n        renderHeader={_ => <View style={{ height: 15 }} />}\n        renderEmptyList={this.renderEmptySessionsList}\n        renderFooter={_ => <F8TimelineBackground height={80} />}\n        navigator={this.props.navigator}\n      />,\n      <ScheduleListView\n        key={\"MSV_SLV_d2\"}\n        title=\"Day 2\"\n        day={2}\n        sessions={sessions}\n        renderHeader={_ => <View style={{ height: 15 }} />}\n        renderEmptyList={this.renderEmptySessionsList}\n        renderFooter={_ => <F8TimelineBackground height={80} />}\n        navigator={this.props.navigator}\n      />,\n      <FriendsListView\n        key={\"MSV_FLV\"}\n        title=\"Friends\"\n        loggedIn={this.props.user.isLoggedIn}\n        friends={this.props.friends}\n        navigator={this.props.navigator}\n      />\n    ];\n  }\n\n  renderHeaderContent() {\n    const { user } = this.props;\n    const isPrivate = user && user.isLoggedIn && !user.sharedSchedule;\n    const privateStyles = isPrivate ? styles.titlePrivate : null;\n    let privacyIcon;\n    if (isPrivate) {\n      privacyIcon = (\n        <PrivacyIcon\n          size={PRIVACY_ICON_SIZE}\n          backgroundColor={F8Colors.colorWithAlpha(\"tangaroa\", 0.5)}\n          iconColor={F8Colors.white}\n          onPress={_ => {\n            this._titleContentWrapper.measureInWindow((x, y, width, height) => {\n              const tooltipX = Math.round(x + width - PRIVACY_ICON_SIZE / 2),\n                tooltipY = Math.round(y + height);\n              this.setState({ tooltipX, tooltipY, privacyModal: true });\n            });\n          }}\n        />\n      );\n    }\n\n    return (\n      <View style={styles.titleContainer}>\n        <View\n          style={styles.titleContent}\n          ref={c => (this._titleContentWrapper = c)}\n          collapsable={false}\n        >\n          <HeaderTitle style={[styles.titleText, privateStyles]}>\n            My F8\n          </HeaderTitle>\n          {privacyIcon}\n        </View>\n      </View>\n    );\n  }\n\n  renderStickyHeader() {\n    if (this.state.showStickyHeader && sessionsHappeningToday(this.props.now)) {\n      return (\n        <HideCompleted\n          backgroundColor={F8Colors.darkPink}\n          onChange={hideCompleted => this.setState({ hideCompleted })}\n          enabled={this.state.hideCompleted}\n        />\n      );\n    } else {\n      return null;\n    }\n  }\n\n  renderNotLoggedIn(containerHeight: number) {\n    if (containerHeight === 0) {\n      return null;\n    }\n    return (\n      <EmptySchedule\n        style={{ height: containerHeight }}\n        key=\"login\"\n        title=\"Log in to make a schedule.\"\n        text=\"You’ll be able to save sessions to your schedule to view or share later.\"\n      >\n        <LoginButton source=\"My F8\" />\n      </EmptySchedule>\n    );\n  }\n\n  renderEmptySessionsList(day: number, containerHeight: number) {\n    if (!this.props.user.isLoggedIn) {\n      return this.renderNotLoggedIn(containerHeight);\n    }\n\n    const todaySessions = this.props.sessions.filter(s => s.day === day);\n    const todayIncomplete = FilterSessions.byCompleted(\n      todaySessions,\n      this.props.now\n    );\n\n    if (todaySessions.length > 0 && todayIncomplete.length === 0) {\n      // there are sessions but they're complete\n      return (\n        <EmptySchedule\n          style={containerHeight > 0 ? { height: containerHeight } : null}\n          key=\"schedule\"\n          text={`Your Day ${day} sessions have completed.\\nThanks for joining us!`}\n        />\n      );\n    } else if (day === 1) {\n      return (\n        <EmptySchedule\n          style={containerHeight > 0 ? { height: containerHeight } : null}\n          key=\"schedule\"\n          image={require(\"./img/empty-header-1.png\")}\n          text={`You haven’t added\\nany Day ${day} sessions yet.`}\n        >\n          <F8Button\n            theme=\"bordered\"\n            fontSize={13}\n            opacity={0.6}\n            caption={`See the day ${day} schedule`}\n            onPress={_ => this.props.jumpToSchedule(1)}\n          />\n        </EmptySchedule>\n      );\n    } else {\n      return (\n        <EmptySchedule\n          style={containerHeight > 0 ? { height: containerHeight } : null}\n          key=\"schedule\"\n          image={require(\"./img/empty-header-2.png\")}\n          text={\"Sessions you add\\nwill appear here.\"}\n        >\n          <F8Button\n            theme=\"bordered\"\n            fontSize={13}\n            opacity={0.6}\n            caption={`See the day ${day} schedule`}\n            onPress={_ => this.props.jumpToSchedule(2)}\n          />\n        </EmptySchedule>\n      );\n    }\n  }\n\n  renderPrivacyModal() {\n    return (\n      <F8Tooltip\n        x={this.state.tooltipX}\n        y={this.state.tooltipY}\n        visible={this.state.privacyModal}\n        title=\"Your schedule is private.\"\n        text=\"Visit Settings to let friends view your schedule.\"\n        onDismiss={_ => this.setState({ privacyModal: false })}\n      />\n    );\n  }\n\n  openSharingSettings() {\n    this.props.navigator.push({ shareSettings: 1 });\n  }\n\n  dismiss() {\n    this.props.navigator.pop();\n  }\n\n  handleSegmentChanged(segment: number) {\n    if (segment === 2 /* friends */) {\n      const { user, loadFriends } = this.props;\n      user.isLoggedIn && loadFriends();\n      this.setState({ showStickyHeader: false });\n    } else if (!this.state.showStickyHeader) {\n      this.setState({ showStickyHeader: true });\n    }\n  }\n}\n\nconst styles = StyleSheet.create({\n  titleContainer: {\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    flex: 1\n  },\n  titleContent: {\n    alignItems: \"center\",\n    alignSelf: \"flex-start\",\n    flexDirection: \"row\"\n  },\n  titleText: {\n    color: F8Colors.yellow\n  },\n  titlePrivate: {\n    marginRight: 9,\n\n    ios: {\n      marginLeft: 29 // fake centering\n    }\n  }\n});\n\nconst data = createSelector(\n  store => store.sessions,\n  store => store.schedule,\n  (sessions, schedule) => FilterSessions.bySchedule(sessions, schedule)\n);\n\nfunction select(store) {\n  return {\n    user: store.user,\n    sessions: data(store),\n    schedule: store.schedule,\n    // Only show friends who have something in their schedule\n    friends: store.friendsSchedules.filter(\n      friend => friend.schedule && Object.keys(friend.schedule).length > 0\n    )\n  };\n}\n\nfunction actions(dispatch) {\n  return {\n    logOut: () => dispatch(logOutWithPrompt()),\n    jumpToSchedule: day => dispatch([switchTab(\"schedule\"), switchDay(day)]),\n    loadFriends: () => dispatch(loadFriendsSchedules())\n  };\n}\n\nmodule.exports = connect(select, actions)(MyScheduleView);\n"
  },
  {
    "path": "js/tabs/schedule/PrivacyIcon.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\nimport React from \"react\";\nimport { View, Image, TouchableOpacity, Alert } from \"react-native\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\n\n/* Config\n============================================================================= */\n\ntype Props = {\n  network: string,\n  password: string\n};\n\ntype State = {};\n\nconst DEFAULT_SIZE = 48,\n  DEFAULT_BGCOLOR = \"#374B71\",\n  ICON_COLOR = \"white\",\n  ICON_WIDTH = 20,\n  ICON_HEIGHT = 25,\n  ICON_WIDTH_RATIO = ICON_WIDTH / DEFAULT_SIZE,\n  ICON_HEIGHT_RATIO = ICON_HEIGHT / DEFAULT_SIZE;\n\n/* =============================================================================\n<PrivacyIcon />\n--------------------------------------------------------------------------------\n\nProps:\n ? size:number             -> Icon size\n ? backgroundColor:string  -> WiFi network password\n ? onPress:function        -> Callback\n ? style                   -> Pass-through container styles\n\n============================================================================= */\n\nclass PrivacyIcon extends React.Component {\n  props: Props;\n  state: State = {};\n\n  static defaultProps = {\n    size: DEFAULT_SIZE,\n    backgroundColor: DEFAULT_BGCOLOR,\n    iconColor: ICON_COLOR\n  };\n\n  render() {\n    const { onPress, backgroundColor, size, iconColor } = this.props;\n    const iconWidth = size * ICON_WIDTH_RATIO;\n    const iconHeight = size * ICON_HEIGHT_RATIO;\n\n    const containerStyles = [\n      styles.container,\n      { backgroundColor, width: size, height: size, borderRadius: size / 2 },\n      this.props.style\n    ];\n\n    if (onPress) {\n      return (\n        <TouchableOpacity\n          activeOpacity={0.8}\n          onPress={onPress}\n          style={containerStyles}\n        >\n          {this.renderIconImage(iconWidth, iconHeight, iconColor)}\n        </TouchableOpacity>\n      );\n    } else {\n      return (\n        <View style={containerStyles}>\n          {this.renderIconImage(iconWidth, iconHeight, iconColor)}\n        </View>\n      );\n    }\n  }\n\n  renderIconImage(width, height, tintColor) {\n    return (\n      <Image\n        style={{ width, height, tintColor }}\n        source={require(\"../../common/img/privacy.png\")}\n      />\n    );\n  }\n}\n\n/* Styles\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  }\n});\n\n/* Playground Cards\n============================================================================= */\n\nconst privacyIcon = PrivacyIcon;\nprivacyIcon.__cards__ = define => {\n  define(\"Default\", () => <PrivacyIcon />);\n  define(\"Touchable\", () => (\n    <PrivacyIcon onPress={() => Alert.alert(\"<PrivacyIcon /> pressed!\")} />\n  ));\n\n  define(\"Customized\", () => (\n    <PrivacyIcon\n      size={20}\n      backgroundColor=\"yellow\"\n      iconColor=\"black\"\n      onPress={() => Alert.alert(\"<PrivacyIcon /> pressed!\")}\n    />\n  ));\n};\n\n/* Export\n============================================================================= */\nmodule.exports = privacyIcon;\n"
  },
  {
    "path": "js/tabs/schedule/ProfileButton.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Image, StyleSheet } from \"react-native\";\n\nclass ProfileButton extends React.Component {\n  render() {\n    return (\n      <Image\n        source={{\n          uri: `https://graph.facebook.com/${this.props.user.id}/picture`\n        }}\n        style={styles.profilePic}\n      />\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  profilePic: {\n    width: 30,\n    height: 30,\n    borderRadius: 15\n  }\n});\n\nmodule.exports = ProfileButton;\n"
  },
  {
    "path": "js/tabs/schedule/ScheduleListView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport F8SessionCell from \"./F8SessionCell\";\nimport FilterSessions from \"./filterSessions\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport React from \"react\";\nimport SessionsSectionHeader from \"./SessionsSectionHeader\";\nimport PureListView from \"../../common/PureListView\";\nimport groupSessions from \"./groupSessions\";\n\nimport type { Session } from \"../../reducers/sessions\";\nimport type { SessionsListData } from \"./groupSessions\";\n\ntype Props = {\n  day: number,\n  sessions: Array<Session>,\n  navigator: Navigator,\n  renderEmptyList?: (day: number) => ReactElement\n};\n\ntype State = {\n  todaySessions: SessionsListData\n};\n\nclass ScheduleListView extends React.Component {\n  props: Props;\n  state: State;\n  _innerRef: ?PureListView;\n\n  constructor(props: Props) {\n    super(props);\n    this.state = {\n      todaySessions: groupSessions(\n        FilterSessions.byDay(props.sessions, props.day)\n      )\n    };\n\n    this._innerRef = null;\n\n    (this: any).renderSectionHeader = this.renderSectionHeader.bind(this);\n    (this: any).renderRow = this.renderRow.bind(this);\n    (this: any).renderEmptyList = this.renderEmptyList.bind(this);\n    (this: any).storeInnerRef = this.storeInnerRef.bind(this);\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    if (\n      nextProps.sessions !== this.props.sessions ||\n      nextProps.day !== this.props.day\n    ) {\n      this.setState({\n        todaySessions: groupSessions(\n          FilterSessions.byDay(nextProps.sessions, nextProps.day)\n        )\n      });\n    }\n  }\n\n  render() {\n    return (\n      <PureListView\n        ref={this.storeInnerRef}\n        data={this.state.todaySessions}\n        renderRow={this.renderRow}\n        renderSectionHeader={this.renderSectionHeader}\n        {...(this.props: any) /* flow can't guarantee the shape of props */}\n        renderEmptyList={this.renderEmptyList}\n      />\n    );\n  }\n\n  renderSectionHeader(sectionData: any, sectionID: string) {\n    let formatted =\n      sectionID\n        .toLowerCase()\n        .replace(\"am\", \"\")\n        .replace(\"pm\", \"\") || sectionID;\n    return <SessionsSectionHeader title={formatted} />;\n  }\n\n  renderRow(session: Session, day: number) {\n    return (\n      <F8SessionCell\n        onPress={_ => this.openSession(session, day)}\n        session={session}\n        firstRow={this.isFirstSessionCell(session.id)}\n      />\n    );\n  }\n\n  renderEmptyList(containerHeight: number): ?ReactElement {\n    // if listview onLayout hasn't updated container height, don't bother\n    if (containerHeight === 0) {\n      return null;\n    } // TODO: different fix\n    // otherwise render fallback cta with a valid and centerable height\n    const { renderEmptyList } = this.props;\n    return renderEmptyList && renderEmptyList(this.props.day, containerHeight);\n  }\n\n  openSession(session: Session, day: number) {\n    let allSessions = { ...this.state.todaySessions };\n    this.props.navigator.push({\n      day,\n      session,\n      allSessions\n    });\n  }\n\n  storeInnerRef(ref: ?PureListView) {\n    this._innerRef = ref;\n  }\n\n  scrollTo(...args: Array<any>) {\n    this._innerRef && this._innerRef.scrollTo(...args);\n  }\n\n  getScrollResponder(): any {\n    return this._innerRef && this._innerRef.getScrollResponder();\n  }\n\n  isFirstSessionCell(id) {\n    const keys = Object.keys(this.state.todaySessions);\n    const innerKeys = Object.keys(this.state.todaySessions[keys[0]]);\n    return id === innerKeys[0];\n  }\n}\n\nmodule.exports = ScheduleListView;\n"
  },
  {
    "path": "js/tabs/schedule/SessionsCarousel.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport F8Analytics from \"../../F8Analytics\";\nimport React from \"react\";\nimport { Platform, StatusBar } from \"react-native\";\nimport F8SessionDetails from \"./F8SessionDetails\";\nimport F8PageControl from \"../../common/F8PageControl\";\nimport F8Header from \"../../common/F8Header\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport formatTime from \"./formatTime\";\nimport Carousel from \"../../common/Carousel\";\nimport { connect } from \"react-redux\";\nimport { loadFriendsSchedules, shareSession } from \"../../actions\";\n\nimport type { Dispatch } from \"../../actions/types\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\n\nimport { Text, View, Navigator } from \"react-native\";\n\nimport type { Session } from \"../../reducers/sessions\";\n\ntype Context = {\n  rowIndex: number, // TODO: IndexWithinSection\n  sectionLength: number,\n  sectionTitle: string\n};\n\ntype Props = {\n  allSessions?: { [sectionID: string]: { [sessionID: string]: Session } },\n  session: Session,\n  navigator: Navigator,\n  dispatch: Dispatch\n};\n\nclass SessionsCarusel extends React.Component {\n  props: Props;\n  state: {\n    day: number,\n    count: number,\n    selectedIndex: number,\n    flatSessionsList: Array<Session>,\n    contexts: Array<Context>\n  };\n\n  constructor(props: Props) {\n    super(props);\n\n    const flatSessionsList = [];\n    const contexts: Array<Context> = [];\n    let allSessions = this.props.allSessions;\n    if (!allSessions) {\n      const { session } = this.props;\n      allSessions = {\n        [formatTime(session.startTime)]: { [session.id]: session }\n      };\n    }\n\n    // TODO: Add test\n    for (let sectionID in allSessions) {\n      const sectionLength = Object.keys(allSessions[sectionID]).length;\n      let rowIndex = 0;\n      for (let sessionID in allSessions[sectionID]) {\n        const session = allSessions[sectionID][sessionID];\n        flatSessionsList.push(session);\n        contexts.push({\n          rowIndex,\n          sectionLength,\n          sectionTitle: sectionID\n        });\n        rowIndex++;\n      }\n    }\n\n    const selectedIndex = flatSessionsList.findIndex(\n      s => s.id === this.props.session.id\n    );\n\n    this.state = {\n      day: this.props.session.day,\n      count: flatSessionsList.length,\n      selectedIndex,\n      flatSessionsList,\n      contexts\n    };\n    (this: any).dismiss = this.dismiss.bind(this);\n    (this: any).handleIndexChange = this.handleIndexChange.bind(this);\n    (this: any).renderCard = this.renderCard.bind(this);\n    (this: any).shareCurrentSession = this.shareCurrentSession.bind(this);\n  }\n\n  render() {\n    const { rowIndex, sectionLength, sectionTitle } = this.state.contexts[\n      this.state.selectedIndex\n    ];\n\n    const backItem = {\n      title: \"Back\",\n      layout: \"icon\",\n      icon: require(\"../../common/img/header/back.png\"),\n      onPress: _ => this.props.navigator.pop()\n    };\n\n    const rightItem = {\n      title: \"Share\",\n      layout: \"icon\",\n      icon: require(\"../../common/img/header/share.png\"),\n      onPress: this.shareCurrentSession\n    };\n\n    return (\n      <View style={styles.container}>\n        <StatusBar barStyle=\"light-content\" animated={true} />\n        <F8Header\n          backgroundColor={F8Colors.palatinateBlue}\n          itemsColor={F8Colors.white}\n          navItem={backItem}\n          rightItem={rightItem}\n          style={Platform.OS === \"ios\" ? { height: 70 } : {}}\n        >\n          <View style={styles.headerContent}>\n            <Text style={styles.day}>{`DAY ${this.state.day}`}</Text>\n            <Text style={styles.time}>{sectionTitle.toLowerCase()}</Text>\n            <F8PageControl count={sectionLength} selectedIndex={rowIndex} />\n          </View>\n        </F8Header>\n        <Carousel\n          count={this.state.count}\n          selectedIndex={this.state.selectedIndex}\n          onSelectedIndexChange={this.handleIndexChange}\n          renderCard={this.renderCard}\n        />\n      </View>\n    );\n  }\n\n  renderCard(index: number): ReactElement {\n    // const iOSKey = Platform.OS === 'ios' ? { key: `SCC_${this.state.flatSessionsList[index].id}`} : {};\n    return (\n      <F8SessionDetails\n        navigator={this.props.navigator}\n        session={this.state.flatSessionsList[index]}\n      />\n    );\n  }\n\n  shareCurrentSession() {\n    const session = this.state.flatSessionsList[this.state.selectedIndex];\n    this.props.dispatch(shareSession(session));\n  }\n\n  componentDidMount() {\n    this.track(this.state.selectedIndex);\n    this.props.dispatch(loadFriendsSchedules());\n  }\n\n  dismiss() {\n    this.props.navigator.pop();\n  }\n\n  handleIndexChange(selectedIndex: number) {\n    this.track(selectedIndex);\n    this.setState({ selectedIndex });\n  }\n\n  track(index: number) {\n    const { id } = this.state.flatSessionsList[index];\n    F8Analytics.logEvent(\"View Session\", 1, { id });\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: F8Colors.white\n  },\n  headerContent: {\n    android: {\n      flex: 1,\n      alignItems: \"flex-start\",\n      justifyContent: \"flex-end\",\n      paddingBottom: 9\n    },\n    ios: {\n      marginTop: -5,\n      alignItems: \"center\"\n      // justifyContent: 'center',\n    }\n  },\n  title: {\n    ios: {\n      textAlign: \"center\"\n    }\n  },\n  day: {\n    color: F8Colors.yellow,\n    fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, \"helveticaBold\"),\n    fontSize: 13,\n\n    android: {\n      marginBottom: -4\n    }\n  },\n  time: {\n    color: F8Colors.white,\n    fontFamily: F8Fonts.fontWithWeight(\"helvetica\", \"semibold\"),\n    fontSize: 15,\n\n    ios: {\n      marginVertical: 2\n    },\n    android: {\n      marginBottom: 3\n    }\n  }\n});\n\nmodule.exports = connect()(SessionsCarusel);\n"
  },
  {
    "path": "js/tabs/schedule/SessionsSectionHeader.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Fonts from \"../../common/F8Fonts\";\nimport React from \"react\";\nimport { View, StyleSheet } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\n\nclass SessionsSectionHeader extends React.Component {\n  props: {\n    title: string\n  };\n\n  render() {\n    return (\n      <View style={styles.header}>\n        <Text style={styles.label}>{this.props.title}</Text>\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  header: {\n    // position:'relative',\n    left: 0,\n    width: 56,\n    marginBottom: -30,\n    justifyContent: \"center\",\n    height: 30\n  },\n  label: {\n    paddingTop: 6,\n    textAlign: \"right\",\n    fontFamily: F8Fonts.helvetica,\n    fontSize: 13,\n    color: F8Colors.tangaroa\n  }\n});\n\nmodule.exports = SessionsSectionHeader;\n"
  },
  {
    "path": "js/tabs/schedule/SharingSettingsCommon.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { View, StyleSheet } from \"react-native\";\nimport F8Colors from \"../../common/F8Colors\";\nimport PrivacyIcon from \"./PrivacyIcon\";\nimport { Heading2, Paragraph } from \"../../common/F8Text\";\nimport ProfilePicture from \"../../common/ProfilePicture\";\nimport { connect } from \"react-redux\";\nimport type { State as User } from \"../../reducers/user\";\n\nclass SharingSettingsCommon extends React.Component {\n  props: {\n    user: User,\n    style: any\n  };\n\n  static defaultProps = {\n    pictureSize: 158\n  };\n\n  render() {\n    const { user, pictureSize } = this.props;\n    const isPrivate = user && !user.sharedSchedule;\n\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <View style={{ paddingBottom: 6 }}>\n          <ProfilePicture userID={user.id} size={pictureSize} />\n          {isPrivate ? <PrivacyIcon style={styles.privacy} /> : null}\n        </View>\n\n        <View style={styles.content}>\n          <Heading2 style={styles.h2}>\n            {\"Let friends view your\\nschedule in the F8 app?\"}\n          </Heading2>\n          <Paragraph style={styles.p}>\n            Friends using the F8 app will be able to view your schedule. This\n            won’t post to Facebook.\n          </Paragraph>\n        </View>\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    alignItems: \"center\"\n  },\n  image: {\n    alignSelf: \"center\"\n  },\n  privacy: {\n    position: \"absolute\",\n    right: 0,\n    bottom: 0\n  },\n  content: {\n    padding: 18,\n    alignItems: \"center\"\n  },\n  h2: {\n    color: F8Colors.blue,\n    textAlign: \"center\"\n  },\n  p: {\n    marginTop: 10,\n    textAlign: \"center\"\n  },\n  title: {\n    marginTop: 40,\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    backgroundColor: \"transparent\"\n  },\n  name: {\n    fontSize: 12,\n    color: \"white\",\n    marginLeft: 10,\n    fontWeight: \"bold\"\n  }\n});\n\nfunction select(store) {\n  return {\n    user: store.user\n  };\n}\n\nmodule.exports = connect(select)(SharingSettingsCommon);\n"
  },
  {
    "path": "js/tabs/schedule/SharingSettingsModal.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Dimensions, StyleSheet, View, Image } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Button from \"../../common/F8Button\";\nimport { Heading2, Paragraph } from \"../../common/F8Text\";\nimport F8BackgroundRepeat from \"../../common/F8BackgroundRepeat\";\nimport ProfilePicture from \"../../common/ProfilePicture\";\nimport FriendsUsingApp from \"./FriendsUsingApp\";\nimport { setSharingEnabled } from \"../../actions\";\nimport { connect } from \"react-redux\";\nimport F8Modal from \"../../common/F8Modal\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  WINDOW_HEIGHT = Dimensions.get(\"window\").height,\n  VERTICAL_BREAKPOINT = WINDOW_HEIGHT <= 600,\n  CONTENT_SPACING_SCALE = VERTICAL_BREAKPOINT ? 0.5 : 1,\n  MODAL_PADDING_H = 10,\n  MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2,\n  HEADER_HEIGHT = 177,\n  PROFILE_PICTURE_SIZE = 70;\n\n/* <SharingSettingsModal />\n============================================================================= */\n\nclass SharingSettingsModal extends React.Component {\n  props: {\n    navigator: Navigator,\n    dispatch: () => void\n  };\n\n  render() {\n    return (\n      <F8Modal\n        renderContent={this.renderContent}\n        renderFooter={this.renderFooter}\n        bottomGradient={[\n          F8Colors.colorWithAlpha(\"tangaroa\", 0),\n          F8Colors.colorWithAlpha(\"tangaroa\", 1)\n        ]}\n        {...this.props}\n      />\n    );\n  }\n\n  renderContent = _ => {\n    return (\n      <View>\n        <View style={styles.header}>\n          <F8BackgroundRepeat\n            width={MODAL_WIDTH}\n            height={HEADER_HEIGHT - 33}\n            source={require(\"../../common/img/pattern-dots.png\")}\n            style={styles.headerBackground}\n          />\n          <View style={styles.headerIllustration}>\n            <Image source={require(\"./img/sharing-nux.png\")} />\n            <View style={styles.profilePicture}>\n              <ProfilePicture\n                userID={this.props.user.id}\n                size={PROFILE_PICTURE_SIZE}\n              />\n            </View>\n          </View>\n        </View>\n\n        <View style={styles.content}>\n          <Heading2 style={styles.h2}>\n            {\"Let friends view your\\nschedule in the F8 app?\"}\n          </Heading2>\n          <Paragraph style={styles.p}>\n            Friends using the F8 app will be able to view your custom schedule.\n          </Paragraph>\n          <F8Button\n            style={styles.button}\n            caption=\"OK\"\n            onPress={() => this.handleSetSharing(true)}\n          />\n          <F8Button\n            theme=\"transparent\"\n            opacity={0.5}\n            caption=\"Maybe later\"\n            onPress={() => this.handleSetSharing(false)}\n          />\n        </View>\n      </View>\n    );\n  };\n\n  renderFooter = _ => {\n    return (\n      <FriendsUsingApp\n        style={{ marginBottom: 20 }}\n        photoBorderColor={F8Colors.colorWithAlpha(\"tangaroa\", 0.8)}\n        textColor={F8Colors.colorWithAlpha(\"white\", 0.5)}\n      />\n    );\n  };\n\n  handleSetSharing(enabled: boolean) {\n    this.props.dispatch(setSharingEnabled(enabled));\n    this.props.onSetSharing && this.props.onSetSharing(enabled);\n    // this.props.navigator.pop();\n  }\n}\n\n/* StyleSheet =============================================================== */\n\nconst styles = StyleSheet.create({\n  header: {\n    alignSelf: \"stretch\"\n  },\n  headerBackground: {\n    position: \"absolute\",\n    left: 0,\n    top: 0\n  },\n  headerIllustration: {\n    alignSelf: \"center\",\n    width: 275,\n    height: HEADER_HEIGHT\n  },\n  profilePicture: {\n    position: \"absolute\",\n    left: 0,\n    right: 0,\n    bottom: 0,\n    alignItems: \"center\"\n  },\n\n  content: {\n    paddingVertical: 26 * CONTENT_SPACING_SCALE,\n    paddingHorizontal: 30\n  },\n\n  h2: {\n    color: F8Colors.blue,\n    textAlign: \"center\",\n    marginBottom: 10\n  },\n  p: {\n    textAlign: \"center\",\n    marginBottom: 15\n  },\n\n  button: {\n    marginTop: 20 * CONTENT_SPACING_SCALE,\n    marginBottom: 10 * CONTENT_SPACING_SCALE,\n    alignSelf: \"stretch\"\n  }\n});\n\n/* redux select ============================================================= */\n\nfunction select(store) {\n  return {\n    user: store.user\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select)(SharingSettingsModal);\n"
  },
  {
    "path": "js/tabs/schedule/SharingSettingsScreen.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport F8Colors from \"../../common/F8Colors\";\nimport React from \"react\";\nimport { Dimensions, View, Switch, StyleSheet } from \"react-native\";\nimport { Navigator } from \"react-native-deprecated-custom-components\";\nimport { Text } from \"../../common/F8Text\";\nimport FriendsUsingApp from \"./FriendsUsingApp\";\nimport F8Header from \"../../common/F8Header\";\nimport SharingSettingsCommon from \"./SharingSettingsCommon\";\nimport { setSharingEnabled, logOutWithPrompt } from \"../../actions\";\nimport { connect } from \"react-redux\";\n\nimport type { State as User } from \"../../reducers/user\";\n\n// more compact on shorter devices\nconst WIN_HEIGHT = Dimensions.get(\"window\").height;\nconst FRIENDS_AREA_SCALING = WIN_HEIGHT <= 600 ? 0.5 : 1;\nconst PROFILE_PICTURE_SIZE = WIN_HEIGHT <= 600 ? 120 : 158;\n\nclass SharingSettingsScreen extends React.Component {\n  props: {\n    navigator: Navigator,\n    dispatch: () => void,\n    sharedSchedule: boolean,\n    user: User\n  };\n\n  render() {\n    const navItem = {\n      icon: require(\"../../common/img/header/back.png\"),\n      title: \"Back\",\n      layout: \"icon\",\n      onPress: () => this.props.navigator.pop()\n    };\n    const rightItem = {\n      icon: require(\"../../common/img/header/logout.png\"),\n      title: \"Log out\",\n      onPress: () => this.props.dispatch(logOutWithPrompt())\n    };\n\n    return (\n      <View\n        style={{\n          flex: 1,\n          backgroundColor: F8Colors.bianca\n        }}\n      >\n        <F8Header\n          title=\"Settings\"\n          backgroundColor={F8Colors.pink}\n          titleColor={F8Colors.white}\n          navItem={navItem}\n          rightItem={rightItem}\n        />\n        <View style={styles.container}>\n          <SharingSettingsCommon pictureSize={PROFILE_PICTURE_SIZE} />\n          <View style={styles.switchWrapper}>\n            <Text style={styles.option}>NO</Text>\n            <Switch\n              accessibilityLabel=\"Let friends view your schedule\"\n              style={styles.switch}\n              value={!!this.props.sharedSchedule}\n              onValueChange={enabled =>\n                this.props.dispatch(setSharingEnabled(enabled))}\n              onTintColor={F8Colors.green}\n            />\n            <Text style={styles.option}>YES</Text>\n          </View>\n        </View>\n        <View style={styles.friends}>\n          <FriendsUsingApp multiline={true} />\n        </View>\n      </View>\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    justifyContent: \"center\",\n    alignItems: \"center\"\n  },\n  switchWrapper: {\n    flexDirection: \"row\",\n    alignItems: \"center\"\n  },\n  switch: {\n    margin: 10\n  },\n  option: {\n    fontSize: 12,\n    color: F8Colors.lightText\n  },\n\n  friends: {\n    paddingTop: 20 * FRIENDS_AREA_SCALING,\n    paddingHorizontal: 50 * FRIENDS_AREA_SCALING,\n    paddingBottom: 40 * FRIENDS_AREA_SCALING\n  }\n});\n\nfunction select(store) {\n  return {\n    user: store.user,\n    sharedSchedule: store.user.sharedSchedule\n  };\n}\n\nmodule.exports = connect(select)(SharingSettingsScreen);\n"
  },
  {
    "path": "js/tabs/schedule/__tests__/formatDuration-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\njest.autoMockOff();\nimport formatDuration from \"../formatDuration\";\n\ndescribe(\"formatDuration\", () => {\n  it(\"formats duration\", () => {\n    expect(formatDuration(0, 3600000)).toEqual(\"1 hour\");\n    expect(formatDuration(0, 7200000)).toEqual(\"2 hours\");\n    expect(formatDuration(0, 1800000)).toEqual(\"30 min\");\n    expect(formatDuration(0, 3601000)).toEqual(\"1 hour 1 min\");\n    expect(formatDuration(0, 1427371200000)).toEqual(\"Until 5:00am\");\n  });\n});\n"
  },
  {
    "path": "js/tabs/schedule/__tests__/formatTime-test.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\n\"use strict\";\n\njest.dontMock(\"../formatTime\");\nimport formatTime from \"../formatTime\";\n\ndescribe(\"formatTime\", () => {\n  it(\"formats time\", () => {\n    expect(formatTime(1427371200000)).toEqual(\"5:00am\");\n    expect(formatTime(1427373900000)).toEqual(\"5:45am\");\n  });\n});\n"
  },
  {
    "path": "js/tabs/schedule/filterSessions.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport type { Session } from \"../../reducers/sessions\";\n\ntype StringMap = { [key: string]: boolean };\n\nfunction byDay(sessions: Array<Session>, day: number): Array<Session> {\n  return sessions.filter(session => session.day === day);\n}\n\nfunction byTopics(sessions: Array<Session>, topics: StringMap): Array<Session> {\n  if (Object.keys(topics).length === 0) {\n    return sessions;\n  }\n  return sessions.filter(session => {\n    let hasMatchingTag = false;\n    session.tags.forEach(tag => {\n      hasMatchingTag = hasMatchingTag || topics[tag];\n    });\n    return hasMatchingTag;\n  });\n}\n\nfunction bySchedule(\n  sessions: Array<Session>,\n  schedule: StringMap\n): Array<Session> {\n  return sessions.filter(session => schedule[session.id]);\n}\n\nfunction byCompleted(sessions: Array<Session>, time: number): Array<Session> {\n  return sessions.filter(session => session.endTime > time);\n}\n\nmodule.exports = { byDay, byTopics, bySchedule, byCompleted };\n"
  },
  {
    "path": "js/tabs/schedule/formatDuration.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport formatTime from \"./formatTime\";\n\nfunction naivePlural(text: string, count: number): string {\n  if (count > 1) {\n    return text + \"s\";\n  }\n  return text;\n}\n\nfunction formatDuration(startMs: number, endMs: number): string {\n  let ms = endMs - startMs;\n  let minutes = ms / 1000 / 60;\n  let hours = Math.floor(minutes / 60);\n\n  if (hours > 2) {\n    return \"Until \" + formatTime(endMs).toLowerCase();\n  }\n\n  let durationText = \"\";\n  if (hours > 0) {\n    durationText = hours + \" \" + naivePlural(\"hour\", hours) + \" \";\n    minutes = minutes - hours * 60;\n  }\n\n  if (minutes > 0) {\n    durationText = durationText + Math.ceil(minutes) + \" min\";\n  }\n\n  return durationText.trim();\n}\n\nmodule.exports = formatDuration;\n"
  },
  {
    "path": "js/tabs/schedule/formatTime.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport moment from \"moment-timezone\";\nimport { timezone } from \"../../env.js\";\n\nfunction formatTime(unix: number, hideAMPM: boolean): string {\n  return moment.tz(unix, timezone).format(hideAMPM ? \"h:mm\" : \"h:mma\");\n}\n\nmodule.exports = formatTime;\n"
  },
  {
    "path": "js/tabs/schedule/groupSessions.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport type { Session } from \"../../reducers/sessions\";\n\nimport formatTime from \"./formatTime\";\n\nexport type SessionsListData = {\n  [time: string]: {\n    [sessionID: string]: Session\n  }\n};\n\nfunction groupSessions(sessions: Array<Session>): SessionsListData {\n  const data = {};\n  sessions.forEach(session => {\n    const timeSectionKey = session.allDay\n      ? \"All Day\"\n      : formatTime(session.startTime);\n    data[timeSectionKey] = data[timeSectionKey] || {};\n    data[timeSectionKey][session.id] = session;\n  });\n\n  return data;\n}\n\nmodule.exports = groupSessions;\n"
  },
  {
    "path": "js/tabs/videos/F8EmptyVideosView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\n/* Dependencies\n============================================================================= */\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Dimensions, View, Image } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Button from \"../../common/F8Button\";\n\nconst HEADER_IMAGE_RATIO = 257 / 375,\n  WIN_WIDTH = Dimensions.get(\"window\").width,\n  HEADER_IMAGE_WIDTH = WIN_WIDTH,\n  HEADER_IMAGE_HEIGHT = HEADER_IMAGE_WIDTH * HEADER_IMAGE_RATIO;\n\n/* <F8EmptyVideosView />\n============================================================================= */\n\nclass F8EmptyVideosView extends React.Component {\n  static defaultProps = {\n    onPress: _ => {}\n  };\n\n  render() {\n    return (\n      <View style={styles.container}>\n        <Image\n          source={require(\"./img/empty-header.png\")}\n          style={[\n            styles.header,\n            { width: HEADER_IMAGE_WIDTH, height: HEADER_IMAGE_HEIGHT }\n          ]}\n        />\n        <View style={styles.content}>\n          <Text style={styles.text}>\n            F8 2017 videos will be added here on the evening of Day 1.\n          </Text>\n          <F8Button\n            theme=\"bordered\"\n            opacity={0.5}\n            caption=\"Watch F8 2016 Videos\"\n            onPress={this.props.onPress}\n          />\n        </View>\n      </View>\n    );\n  }\n}\n\n/* StyleSheet\n============================================================================= */\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    paddingVertical: 20\n  },\n  content: {\n    paddingHorizontal: 46\n  },\n  text: {\n    marginTop: 22,\n    marginBottom: 48,\n    fontSize: 17,\n    lineHeight: 27,\n    color: F8Colors.tangaroa,\n    textAlign: \"center\"\n  }\n});\n\n/* Exports\n============================================================================= */\nexport default F8EmptyVideosView;\n"
  },
  {
    "path": "js/tabs/videos/F8VideoThumb.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Dimensions, View, Image, TouchableOpacity } from \"react-native\";\nimport { Text } from \"../../common/F8Text\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Fonts from \"../../common/F8Fonts\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  CONTAINER_PADDING_H = 15,\n  GUTTER = 8,\n  ROW_SPACING = 40,\n  WIDTH_LARGE = WINDOW_WIDTH - CONTAINER_PADDING_H * 2,\n  WIDTH_SMALL = (WINDOW_WIDTH - CONTAINER_PADDING_H * 2 - GUTTER) / 2,\n  IMAGE_ASPECT_RATIO_SMALL = 99 / 169,\n  IMAGE_ASPECT_RATIO_LARGE = 202 / 344,\n  NUMLINES_SMALL = 3,\n  NUMLINES_LARGE = 2;\n\n/**\n* ==============================================================================\n* <F8VideoThumb />\n* ------------------------------------------------------------------------------\n* @param {?String} type Layout style (default \"small\")\n* @param {?String} title Video title\n* @param {?String} length Video length\n* @param {?String} image Thumbnail image source\n* @param {?Boolean} watched Has video already been watched by user\n* @param {?Number} activeOpacity On-tap opacity value (default 0.75)\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8VideoThumb extends React.Component {\n  static defaultProps = {\n    type: \"small\",\n    activeOpacity: 0.75\n  };\n\n  render() {\n    const {\n      id,\n      type,\n      onPress,\n      image,\n      title,\n      activeOpacity,\n      length,\n      watched\n    } = this.props;\n    const { imageWidth, imageHeight } = this.getImageSize(type);\n\n    return (\n      <TouchableOpacity\n        activeOpacity={activeOpacity}\n        style={type === \"large\" ? styles.containerLarge : styles.containerSmall}\n        onPress={_ => onPress && onPress(id)}\n      >\n        <View style={styles.thumb}>\n          {this.renderImage(image, imageWidth, imageHeight)}\n          {this.renderLength(type, length, watched)}\n        </View>\n        {this.renderTitle(type, title)}\n      </TouchableOpacity>\n    );\n  }\n\n  renderImage(src, width, height) {\n    if (src) {\n      return (\n        <Image\n          style={[styles.image, { width, height }]}\n          source={{ uri: src }}\n        />\n      );\n    } else {\n      return <View style={[styles.image, { width, height }]} />;\n    }\n  }\n\n  renderLength(type, length, watched) {\n    const timeDifferences =\n      type === \"large\" ? styles.timeLarge : styles.timeSmall;\n    if (length) {\n      return <Text style={[styles.time, timeDifferences]}>{length}</Text>;\n    } else {\n      return null;\n    }\n  }\n\n  renderTitle(type, title) {\n    const titleLineLimit = type === \"large\" ? NUMLINES_LARGE : NUMLINES_SMALL;\n    const titleDifferences =\n      type === \"large\" ? styles.titleLarge : styles.titleSmall;\n    if (title) {\n      return (\n        <Text\n          numberOfLines={titleLineLimit}\n          style={[styles.title, titleDifferences]}\n        >\n          {title}\n        </Text>\n      );\n    } else {\n      return null;\n    }\n  }\n\n  getImageSize(type) {\n    return type === \"large\"\n      ? {\n          imageWidth: WIDTH_LARGE,\n          imageHeight: WIDTH_LARGE * IMAGE_ASPECT_RATIO_LARGE\n        }\n      : {\n          imageWidth: WIDTH_SMALL,\n          imageHeight: WIDTH_SMALL * IMAGE_ASPECT_RATIO_SMALL\n        };\n  }\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  containerLarge: {\n    marginVertical: ROW_SPACING / 2,\n    width: WIDTH_LARGE,\n    paddingHorizontal: GUTTER / 2\n  },\n  containerSmall: {\n    marginVertical: ROW_SPACING / 2,\n    width: WIDTH_SMALL + GUTTER,\n    paddingHorizontal: GUTTER / 2\n  },\n\n  image: {\n    backgroundColor: F8Colors.tangaroa,\n    resizeMode: \"cover\"\n  },\n\n  time: {\n    position: \"absolute\",\n    right: 0,\n    bottom: 0,\n    backgroundColor: F8Colors.colorWithAlpha(\"tangaroa\", 0.8),\n    fontFamily: F8Fonts.helvetica,\n    color: \"white\"\n  },\n  timeSmall: {\n    fontSize: 11,\n    paddingVertical: 2,\n    paddingHorizontal: 3\n  },\n  timeLarge: {\n    fontSize: 13,\n    paddingVertical: 3,\n    paddingHorizontal: 5\n  },\n\n  title: {\n    color: F8Colors.black\n  },\n  titleSmall: {\n    marginTop: 9,\n    fontSize: 13,\n    lineHeight: F8Fonts.lineHeight(17)\n  },\n  titleLarge: {\n    marginTop: 12,\n    fontSize: 17,\n    lineHeight: F8Fonts.lineHeight(22)\n  }\n});\n\n/* export =================================================================== */\nexport default F8VideoThumb;\n"
  },
  {
    "path": "js/tabs/videos/F8VideoView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport StyleSheet from \"../../common/F8StyleSheet\";\nimport F8Header from \"../../common/F8Header\";\nimport { StatusBar, View, ScrollView } from \"react-native\";\nimport { Heading2, Paragraph } from \"../../common/F8Text\";\nimport F8VideoPlayer from \"../../video/F8VideoPlayer\";\nimport { connect } from \"react-redux\";\nimport { shareVideo } from \"../../actions\";\n\n/* <F8VideoView />\n============================================================================= */\n\nclass F8VideoView extends React.Component {\n  render() {\n    const { video } = this.props;\n\n    let rightItem;\n    if (video.shareURL) {\n      rightItem = {\n        title: \"Share\",\n        layout: \"icon\",\n        //$FlowFixMe\n        icon: require(\"../../common/img/header/share.png\"),\n        onPress: this.share\n      };\n    }\n\n    return (\n      <View style={styles.view}>\n        <StatusBar hidden={false} barStyle=\"light-content\" animated={true} />\n        <F8Header\n          title=\"Video\"\n          backgroundColor={F8Colors.tangaroa}\n          titleColor={F8Colors.pink}\n          itemsColor={F8Colors.white}\n          navItem={{\n            title: \"Back\",\n            layout: \"icon\",\n            icon: require(\"../../common/img/header/back.png\"),\n            onPress: _ => this.props.navigator.pop()\n          }}\n          rightItem={rightItem}\n        />\n        <View style={styles.content}>\n          <F8VideoPlayer\n            id={video.id}\n            source={video.source}\n            backgroundImage={{ uri: video.image }}\n          />\n          <ScrollView\n            showsVerticalScrollIndicator={false}\n            style={styles.textBlock}\n          >\n            {this.renderTitle()}\n            {this.renderDescription()}\n          </ScrollView>\n        </View>\n      </View>\n    );\n  }\n\n  renderTitle() {\n    const { video } = this.props;\n    if (video.title) {\n      return <Heading2 style={styles.title}>{video.title}</Heading2>;\n    } else {\n      return null;\n    }\n  }\n\n  renderDescription() {\n    const { video } = this.props;\n    if (video.description) {\n      return <Paragraph>{video.description}</Paragraph>;\n    } else {\n      return null;\n    }\n  }\n\n  share = _ => {\n    this.props.dispatch(shareVideo(this.props.video));\n  };\n}\n\n/* StyleSheet =============================================================== */\nconst styles = StyleSheet.create({\n  view: {\n    backgroundColor: F8Colors.bianca,\n    flex: 1\n  },\n  content: {\n    flex: 1\n  },\n\n  textBlock: {\n    paddingVertical: 23,\n    paddingHorizontal: 32\n  },\n  title: {\n    color: F8Colors.blue,\n    marginBottom: 5\n  }\n});\n\n/* exports ================================================================== */\nmodule.exports = connect()(F8VideoView);\n"
  },
  {
    "path": "js/tabs/videos/F8VideosView.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport { Platform, View } from \"react-native\";\nimport ListContainer from \"../../common/ListContainer\";\nimport PureListView from \"../../common/PureListView\";\nimport F8Colors from \"../../common/F8Colors\";\nimport F8Linking from \"../../common/F8Linking\";\nimport F8EmptyVideosView from \"./F8EmptyVideosView\";\nimport F8VideoThumb from \"./F8VideoThumb\";\nimport * as FilterVideos from \"./filterVideos\";\nimport FilterHeader from \"../schedule/FilterHeader\";\nimport FilterScreen from \"../../filter/FilterScreen\";\n\nimport { connect } from \"react-redux\";\nimport { applyVideoFilter, clearVideoFilter } from \"../../actions\";\nimport { createSelector } from \"reselect\";\n\n/**\n* ==============================================================================\n* <F8VideosView />\n* ------------------------------------------------------------------------------\n* @param {Array.<Video>} videos    Parse Video class\n* @param {F8Navigator}   navigator Navigation methods\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8VideosView extends React.Component {\n  constructor() {\n    super();\n\n    this.renderRow = this.renderRow.bind(this);\n    this.onPressEmptyCTA = this.onPressEmptyCTA.bind(this);\n    this.onPress = this.onPress.bind(this);\n    this.openFilterScreen = this.openFilterScreen.bind(this);\n\n    this.state = {\n      year: 2017,\n      filterModal: false\n    };\n  }\n\n  render() {\n    let filterItem;\n    if (this.props.topics && this.props.topics.length) {\n      filterItem = {\n        icon: require(\"../../common/img/header/filter.png\"),\n        title: \"Filter\",\n        onPress: this.openFilterScreen\n      };\n    }\n\n    const content = (\n      <ListContainer\n        headerBackgroundColor={F8Colors.tangaroa}\n        headerTitleColor={F8Colors.pink}\n        title=\"Videos\"\n        stickyHeader={this.renderStickyHeader()}\n        leftItem={{\n          title: \"Map\",\n          layout: \"icon\",\n          icon: require(\"../../common/img/header/map.png\"),\n          onPress: _ =>\n            this.props.navigator && this.props.navigator.push({ maps: true })\n        }}\n        rightItem={filterItem}\n      >\n        <PureListView\n          data={FilterVideos.asListRows(this.props.videos)}\n          renderEmptyList={_ => (\n            <F8EmptyVideosView onPress={this.onPressEmptyCTA} />\n          )}\n          renderRow={this.renderRow}\n        />\n      </ListContainer>\n    );\n\n    if (Platform.OS === \"ios\") {\n      return content;\n    } else {\n      return (\n        <View style={{ flex: 1 }}>\n          {content}\n          <FilterScreen\n            visible={this.state.filterModal}\n            topics={this.props.topics}\n            selectedTopics={this.props.filter}\n            onClose={_ => this.setState({ filterModal: false })}\n            onApply={selected => this.props.filterTopics(selected)}\n          />\n        </View>\n      );\n    }\n  }\n\n  renderRow(row: Array, sid, rid) {\n    const largeVideo = row[0] && row[0].type === \"large\";\n    const content = row.map((video, idx) => (\n      <F8VideoThumb\n        key={`vlt_${video.id}`}\n        type={largeVideo ? \"large\" : \"small\"}\n        onPress={this.onPress}\n        {...video}\n      />\n    ));\n    return (\n      <View\n        style={{\n          flexDirection: \"row\",\n          alignItems: \"flex-start\",\n          justifyContent: \"flex-start\",\n          paddingLeft: 11\n        }}\n      >\n        {content}\n      </View>\n    );\n  }\n\n  renderStickyHeader() {\n    if (Object.keys(this.props.filter).length > 0) {\n      return (\n        <FilterHeader\n          backgroundColor={F8Colors.blue}\n          filter={this.props.filter}\n          onClear={_ => this.props.clearFilter()}\n        />\n      );\n    } else {\n      return null;\n    }\n  }\n\n  onPressEmptyCTA() {\n    F8Linking.openURL(\"https://developers.facebook.com/videos/\");\n  }\n\n  onPress(selected) {\n    const video = this.props.videos.find(vid => vid.id === selected);\n    this.props.navigator && this.props.navigator.push({ video });\n  }\n\n  openFilterScreen() {\n    if (Platform.OS === \"ios\") {\n      this.props.navigator.push({\n        filter: true,\n        topics: this.props.topics,\n        selectedTopics: this.props.filter,\n        onApply: selected => this.props.filterTopics(selected)\n      });\n    } else {\n      this.setState({ filterModal: true });\n    }\n  }\n}\n\n/* redux ==================================================================== */\n\nconst data = createSelector(\n  store => store.videos,\n  store => store.videoFilter,\n  (videos, filter) => sortFeatured(FilterVideos.byTopics(videos, filter))\n);\n\nfunction sortFeatured(videos = []) {\n  const other = [],\n    pinned = [];\n  videos.map(video => {\n    if (video.featured) {\n      pinned.push(video);\n    } else {\n      other.push(video);\n    }\n  });\n  return [...pinned, ...other];\n}\n\nfunction actions(dispatch) {\n  return {\n    // switchDay: (day) => dispatch(switchDay(day)),\n    filterTopics: selected => dispatch(applyVideoFilter(selected)),\n    clearFilter: _ => dispatch(clearVideoFilter())\n  };\n}\n\nfunction select(store) {\n  return {\n    videos: data(store),\n    topics: store.videoTopics,\n    filter: store.videoFilter\n  };\n}\n\n/* exports ================================================================== */\nmodule.exports = connect(select, actions)(F8VideosView);\n"
  },
  {
    "path": "js/tabs/videos/filterVideos.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport type { Video } from \"../../reducers/videos\";\n\ntype StringMap = { [key: string]: boolean };\n\n/**\n* ==============================================================================\n* Filter videos by year\n* ------------------------------------------------------------------------------\n* @param {Array.<Video>} videos Collection of videos from Parse class\n* @param {Number} year the year to match against\n* @return {Array}\n* ==============================================================================\n*/\nexport function byYear(videos: Array<Video>, year: number): Array<Video> {\n  return videos.filter(video => video.year === year);\n}\n\n/**\n* ==============================================================================\n* Sort flat list of videos into 1-2-2-2... pattern for list view rendering\n* ------------------------------------------------------------------------------\n* @param {Array.<Video>} videos Collection of videos from Parse class\n* @return {Array.Array.<Video>}\n* ==============================================================================\n*/\nexport function asListRows(\n  videos: Array<Video> = [],\n  splitRowsThreshold: number = 6\n): Array {\n  const rows = [];\n  // pull out featured videos force single row and place at the start of the list\n  rows.push(\n    ...videos.filter(v => v.featured).map(f => [{ type: \"large\", ...f }])\n  );\n  // filter non-featured videos and do 2-up or full-width depending on length\n  const rest = videos.filter(v => !v.featured);\n  if (rest.length >= splitRowsThreshold) {\n    rows.push([]); // start rows\n    rest.map(v => {\n      // if previous row is already max length, start a new one\n      if (rows[rows.length - 1].length === 2) {\n        rows.push([v]);\n      } else {\n        // else add this to an already created row\n        rows[rows.length - 1].push(v);\n      }\n    });\n  } else {\n    rows.push(...rest.map(r => [{ type: \"large\", ...r }])); // 1-up when < minimum split rows length\n  }\n  // if(rows.length && rows[rows.length - 1].length === 0) rows.pop();\n  return rows;\n}\n\n/**\n* ==============================================================================\n* Filter videos by selected topics\n* ------------------------------------------------------------------------------\n* @param {Array.<Video>} videos Collection of videos from Parse class\n* @param {Object} topics { <topic string> : boolean } list of enabled topics\n* @return {Array.<Video>}\n* ==============================================================================\n*/\nexport function byTopics(\n  videos: Array<Video>,\n  topics: StringMap\n): Array<Video> {\n  if (Object.keys(topics).length === 0) {\n    return videos;\n  }\n  return videos.filter(video => {\n    let hasMatchingTag = false;\n    video.tags.forEach(tag => {\n      hasMatchingTag = hasMatchingTag || topics[tag];\n    });\n    return hasMatchingTag;\n  });\n}\n"
  },
  {
    "path": "js/video/F8VideoPlayer.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport {\n  StatusBar,\n  Platform,\n  Dimensions,\n  StyleSheet,\n  View,\n  Image\n} from \"react-native\";\nimport VideoPlayer from \"react-native-native-video-player\";\nimport PlayButton from \"../common/PlayButton\";\nimport F8Analytics from \"../F8Analytics\";\n\n/* constants ================================================================ */\n\nconst WINDOW_WIDTH = Dimensions.get(\"window\").width,\n  VIDEO_ASPECT_RATIO = 16 / 9;\n\n/**\n* ==============================================================================\n* <F8VideoPlayer />\n* ------------------------------------------------------------------------------\n* @param {string}   source   Video URL\n* @param {?boolean} autoplay Inline video player only\n* @param {?number}  width    View width\n* @param {?number}  height   View height\n* @return {ReactElement}\n* ==============================================================================\n*/\n\nclass F8VideoPlayer extends React.Component {\n  static defaultProps = {\n    autoplay: false,\n    backgroundImage: {},\n    width: WINDOW_WIDTH,\n    height: WINDOW_WIDTH / VIDEO_ASPECT_RATIO\n  };\n\n  render() {\n    const { width, height } = this.props;\n\n    return (\n      <View style={[styles.container, { width, height }, this.props.style]}>\n        {this.renderBackgroundImage()}\n        {this.renderPlayButton()}\n      </View>\n    );\n  }\n\n  componentWillUnmount() {\n    // TODO: better fix for ios status disappearance bug\n    Platform.OS === \"ios\" && StatusBar && StatusBar.setHidden(false, true);\n  }\n\n  renderBackgroundImage() {\n    const { backgroundImage } = this.props;\n    if (!backgroundImage.uri) {\n      return null;\n    } else {\n      return <Image source={backgroundImage} style={styles.backgroundImage} />;\n    }\n  }\n\n  renderPlayButton() {\n    return (\n      <View style={styles.stretchToFill}>\n        <PlayButton\n          buttonColor={F8Colors.pink}\n          iconColor={F8Colors.yellow}\n          onPress={this.playVideo}\n        />\n      </View>\n    );\n  }\n\n  playVideo = _ => {\n    F8Analytics.logEvent(\"Video Started\", 1, { id: this.props.id });\n    VideoPlayer.showVideoPlayer(this.props.source);\n  };\n}\n\n/* styles =================================================================== */\n\nconst styles = StyleSheet.create({\n  container: {\n    backgroundColor: F8Colors.black\n  },\n  backgroundImage: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    resizeMode: \"cover\"\n  },\n  stretchToFill: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  }\n});\n\n/* Playground =============================================================== */\n\nconst videoPlayer = F8VideoPlayer;\nvideoPlayer.__cards__ = define => {\n  define(\"Default\", _ => (\n    <F8VideoPlayer\n      videoSource={{\n        uri: \"https://s3-us-west-2.amazonaws.com/f8-dev/test_video_2.mp4\"\n      }}\n      backgroundImage={{\n        uri:\n          \"https://scontent-atl3-1.xx.fbcdn.net/v/t15.0-10/p526x395/12402416_10153638704483553_1401883252_n.jpg?oh=fee763691545a10645d5cd868a221135&oe=58DC9BC9\"\n      }}\n    />\n  ));\n\n  define(\"Autoplay\", _ => (\n    <F8VideoPlayer\n      autoplay={true}\n      videoSource={{\n        uri: \"https://s3-us-west-2.amazonaws.com/f8-dev/test_video_2.mp4\"\n      }}\n      backgroundImage={{\n        uri:\n          \"https://scontent-atl3-1.xx.fbcdn.net/v/t15.0-10/p526x395/12402416_10153638704483553_1401883252_n.jpg?oh=fee763691545a10645d5cd868a221135&oe=58DC9BC9\"\n      }}\n    />\n  ));\n};\n\n/* exports ================================================================== */\n\nmodule.exports = videoPlayer;\n"
  },
  {
    "path": "js/video/VideoControls.js",
    "content": "\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../common/F8Colors\";\nimport F8Button from \"../common/F8Button\";\nimport {\n  TouchableWithoutFeedback,\n  TouchableHighlight,\n  Animated,\n  StyleSheet,\n  View,\n  Image,\n  Platform\n} from \"react-native\";\n\n/* =============================================================================\n<VideoControls />\n============================================================================= */\n\nclass VideoControls extends React.Component {\n  // static defaultProps = {\n  //   show: false,\n  // };\n\n  constructor(props) {\n    super(props);\n\n    this.onHitbox = this.onHitbox.bind(this);\n    this.hitboxTimer;\n\n    this.state = {\n      show: false,\n      revealAnimation: new Animated.Value(1)\n    };\n  }\n\n  render() {\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <TouchableWithoutFeedback onPress={this.onHitbox}>\n          <View style={styles.hitbox} />\n        </TouchableWithoutFeedback>\n        {this.renderActions()}\n      </View>\n    );\n  }\n\n  renderActions() {\n    if (!this.state.show) {\n      return null;\n    }\n    const {\n      onPlayToggle,\n      onSkipBack,\n      onSkipForward,\n      onFullscreen\n    } = this.props;\n\n    return (\n      <Animated.View\n        pointerEvents=\"box-none\"\n        style={[styles.showHide, { opacity: this.state.revealAnimation }]}\n      >\n        <View pointerEvents=\"box-none\" style={styles.actions}>\n          {onSkipBack && onSkipForward ? this.renderSkipButtons() : null}\n          {onPlayToggle ? this.renderPlayButton() : null}\n        </View>\n        {onFullscreen ? this.renderFullscreenButton() : null}\n      </Animated.View>\n    );\n  }\n\n  renderSkipButtons() {\n    return [\n      <F8Button\n        theme=\"white\"\n        type=\"round\"\n        icon={require(\"../common/img/buttons/icon-check.png\")}\n        onPress={_ => this.props.onSkipBack && this.props.onSkipBack()}\n        style={styles.skipBack}\n      />,\n      <F8Button\n        theme=\"white\"\n        type=\"round\"\n        icon={require(\"../common/img/buttons/icon-check.png\")}\n        onPress={_ => this.props.onSkipForward && this.props.onSkipForward()}\n        style={styles.skipForward}\n      />\n    ];\n  }\n\n  renderPlayButton() {\n    return (\n      <PlayPauseButton\n        value={this.props.isPlaying}\n        onPress={_ => this.props.onPlayToggle && this.props.onPlayToggle()}\n      />\n    );\n  }\n\n  renderFullscreenButton() {\n    if (Platform.OS === \"ios\") {\n      return (\n        <F8Button\n          theme=\"white\"\n          type=\"round\"\n          icon={require(\"../common/img/buttons/icon-x.png\")}\n          onPress={_ => this.props.onFullscreen && this.props.onFullscreen()}\n          style={styles.fullscreen}\n        />\n      );\n    } else {\n      return null;\n    }\n  }\n\n  onHitbox() {\n    clearTimeout(this.hitboxTimer);\n    const show = !this.state.show;\n    this.setState({ show });\n    if (show) {\n      this.hitboxTimer = setTimeout(_ => {\n        this.setState({ show: false });\n      }, 3000);\n    }\n  }\n}\n\n/* =============================================================================\n<PlayPauseButton />\n============================================================================= */\n\nclass PlayPauseButton extends React.Component {\n  render() {\n    const { onPress, value } = this.props;\n    const playOpacity = value ? 0 : 1;\n    const pauseOpacity = value ? 1 : 0;\n    return (\n      <TouchableHighlight\n        underlayColor={F8Colors.colorWithAlpha(\"blue\", 0.5)}\n        style={styles.playPause}\n        onPress={_ => onPress && onPress(!value)}\n      >\n        <View style={{ flex: 1 }}>\n          <Image\n            style={[styles.pauseIcon, { opacity: pauseOpacity }]}\n            source={require(\"../common/img/buttons/icon-x.png\")}\n          />\n          <Image\n            style={[styles.playIcon, { opacity: playOpacity }]}\n            source={require(\"../common/img/buttons/play-large.png\")}\n          />\n        </View>\n      </TouchableHighlight>\n    );\n  }\n}\n\n// StyleSheet ==================================================================\n\nconst styles = StyleSheet.create({\n  container: {},\n  hitbox: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0\n  },\n  showHide: {\n    backgroundColor: F8Colors.colorWithAlpha(\"black\", 0.33),\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    right: 0,\n    bottom: 0,\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    overflow: \"hidden\"\n  },\n  actions: {\n    width: 160,\n    alignItems: \"center\"\n  },\n  skipBack: {\n    position: \"absolute\",\n    left: 0,\n    top: (76 - F8Button.height) / 2\n  },\n  skipForward: {\n    position: \"absolute\",\n    right: 0,\n    top: (76 - F8Button.height) / 2\n  },\n  fullscreen: {\n    position: \"absolute\",\n    right: -10,\n    bottom: -10\n  },\n\n  playPause: {\n    width: 72,\n    height: 72,\n    borderRadius: 72 / 2\n  },\n  pauseIcon: {\n    position: \"absolute\",\n    left: 0,\n    top: 0,\n    width: 72,\n    height: 72,\n    tintColor: F8Colors.white,\n    resizeMode: \"center\"\n  },\n  playIcon: {\n    position: \"absolute\",\n    left: 5,\n    top: 0,\n    width: 67,\n    height: 72,\n    tintColor: F8Colors.white,\n    resizeMode: \"center\"\n  }\n});\n\n// export ======================================================================\n\nexport default VideoControls;\n"
  },
  {
    "path": "js/video/VideoLoader.js",
    "content": "\"use strict\";\n\nimport React from \"react\";\nimport F8Colors from \"../../common/F8Colors\";\nimport { Animated, StyleSheet, View } from \"react-native\";\n\nfunction loopingAnimation(animations = []) {\n  if (!animations.length) {\n    return;\n  }\n  Animated.sequence(animations).start(event => {\n    if (event.finished) {\n      loopingAnimation(animations);\n    }\n  });\n}\n\n/* =============================================================================\n<VideoLoader />\n============================================================================= */\n\nclass VideoLoader extends React.Component {\n  static defaultProps = {\n    width: 100\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      pulseAnimation: new Animated.Value(0)\n    };\n  }\n\n  render() {\n    return (\n      <View style={[styles.container, this.props.style]}>\n        <Animated.View\n          style={[styles.loader, { opacity: this.state.pulseAnimation }]}\n        />\n      </View>\n    );\n  }\n\n  componentDidMount() {\n    loopingAnimation([\n      Animated.timing(this.state.pulseAnimation, {\n        toValue: 1,\n        duration: 400,\n        delay: 300\n      }),\n      Animated.timing(this.state.pulseAnimation, {\n        toValue: 0,\n        duration: 400,\n        delay: 300\n      })\n    ]);\n  }\n}\n\n// StyleSheet ==================================================================\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    alignItems: \"center\",\n    justifyContent: \"center\"\n  },\n  loader: {\n    backgroundColor: F8Colors.blue,\n    width: 80,\n    height: 80\n  }\n});\n\n// export ======================================================================\n\nexport default VideoLoader;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"F82017\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"scripts\": {\n    \"server\": \"docker-compose up\",\n    \"android\": \"./scripts/run-android-app.sh\",\n    \"ios\": \"babel-node ./scripts/open-ios-project.js\",\n    \"test\": \"jest --verbose\",\n    \"lint\": \"eslint .\",\n    \"prettier\": \"prettier --write '**/*.js'\",\n    \"prepush\": \"./scripts/pre-push-checks.sh\",\n    \"graphql\": \"babel-node ./scripts/generate-graphql-schema.js\",\n    \"relay\": \"relay-compiler --src ./js --schema ./server/graphql/src/schema/__generated__/schema.graphql\"\n  },\n  \"dependencies\": {\n    \"crc32\": \"^0.2.2\",\n    \"idx\": \"^1.5.0\",\n    \"moment-timezone\": \"^0.5.11\",\n    \"parse\": \"^1.11.0\",\n    \"react\": \"16.0.0-alpha.6\",\n    \"react-native\": \"0.44.0\",\n    \"react-native-deprecated-custom-components\": \"^0.1.1\",\n    \"react-native-fbsdk\": \"0.6.0\",\n    \"react-native-hyperlink\": \"^0.0.7\",\n    \"react-native-keyboard-aware-scroll-view\": \"^0.2.7\",\n    \"react-native-linear-gradient\": \"^2.0.0\",\n    \"react-native-native-video-player\": \"^1.3.3\",\n    \"react-native-photo-view\": \"^1.2.0\",\n    \"react-native-push-notification\": \"^2.2.1\",\n    \"react-native-tab-navigator\": \"^0.3.3\",\n    \"react-redux\": \"^4.4.6\",\n    \"react-relay\": \"^1.4.0\",\n    \"redux\": \"^3.6.0\",\n    \"redux-logger\": \"^2.7.4\",\n    \"redux-persist\": \"^4.0.0-beta1\",\n    \"redux-thunk\": \"^2.1.0\",\n    \"relay-runtime\": \"^1.4.0\",\n    \"reselect\": \"^2.5.4\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.24.1\",\n    \"babel-eslint\": \"^7.2.0\",\n    \"babel-jest\": \"17.0.2\",\n    \"babel-plugin-relay\": \"^1.4.0\",\n    \"babel-preset-es2015\": \"6.24.1\",\n    \"babel-preset-react-native\": \"1.9.0\",\n    \"eslint\": \"^4.18.2\",\n    \"eslint-plugin-prettier\": \"^2.3.1\",\n    \"eslint-plugin-react\": \"^6.10.3\",\n    \"flow-bin\": \"0.42\",\n    \"graphql\": \"^0.11.3\",\n    \"graphql-relay\": \"0.5.2\",\n    \"husky\": \"^0.14.3\",\n    \"jest\": \"17.0.3\",\n    \"prettier\": \"^1.7.0\",\n    \"react-test-renderer\": \"15.4.1\",\n    \"relay-compiler\": \"^1.4.0\",\n    \"xcode\": \"^0.9.3\"\n  },\n  \"jest\": {\n    \"preset\": \"react-native\",\n    \"transformIgnorePatterns\": [\n      \"(node_modules)/(?!react-native|parse)\"\n    ],\n    \"transform\": {\n      \"^.+\\\\.js$\": \"node_modules/babel-jest\"\n    }\n  }\n}\n"
  },
  {
    "path": "scripts/generate-graphql-schema.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\nimport { printSchema } from \"graphql/utilities\";\n\nimport schema from \"../server/graphql/src/schema\";\n\nfs.writeFileSync(\n  path.join(\n    __dirname,\n    \"../server/graphql/src/schema/__generated__/schema.graphql\"\n  ),\n  printSchema(schema)\n);\n"
  },
  {
    "path": "scripts/open-ios-project.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst { exec } = require(\"child_process\");\nconst xcode = require(\"xcode\");\n\nconst { HOME } = process.env;\nif (HOME && !fs.existsSync(path.join(HOME, \"Documents/FacebookSDK\"))) {\n  console.log(\"WARNING: Facebook SDK is not found in ~/Documents/FacebookSDK\");\n  console.log(\n    \"Please download Facebook SDK from https://developers.facebook.com/docs/ios/\"\n  );\n  console.log(\"and put it in ~/Documents/FacebookSDK\");\n  process.exit(1);\n}\n\nconst projectPath =\n  \"node_modules/react-native-native-video-player/ios/RNVideoPlayer.xcodeproj/project.pbxproj\";\nconst project = xcode.project(projectPath);\nproject.parse(function() {\n  // Make sure F82017 is in the RNVideoPlayer header search paths, otherwise\n  // there will be a build error.\n  if (!project.writeSync().includes(\"F82017\")) {\n    project.addToHeaderSearchPaths('\"$(SRCROOT)/../../../ios/F82017\"');\n    fs.writeFileSync(projectPath, project.writeSync());\n  }\n  console.log(\"iOS dependencies configured successfully\");\n\n  console.log(\"Opening ./ios/F82017.xcodeproj...\");\n  exec(\"open ./ios/F82017.xcodeproj\");\n});\n"
  },
  {
    "path": "scripts/optimize-images.sh",
    "content": "#!/bin/bash\n\n# Copyright 2016 Facebook, Inc.\n#\n# You are hereby granted a non-exclusive, worldwide, royalty-free license to\n# use, copy, modify, and distribute this software in source code or binary\n# form for use in connection with the web services and APIs provided by\n# Facebook.\n#\n# As with any software that integrates with the Facebook platform, your use\n# of this software is subject to the Facebook Developer Principles and\n# Policies [http://developers.facebook.com/policy/]. This copyright notice\n# shall be included in all copies or substantial portions of the software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n# DEALINGS IN THE SOFTWARE\n\nfind js -name '*.png' -exec pngcrush -ow {} \\;\n"
  },
  {
    "path": "scripts/pre-push-checks.sh",
    "content": "#!/bin/bash\n\n# Copyright 2016 Facebook, Inc.\n#\n# You are hereby granted a non-exclusive, worldwide, royalty-free license to\n# use, copy, modify, and distribute this software in source code or binary\n# form for use in connection with the web services and APIs provided by\n# Facebook.\n#\n# As with any software that integrates with the Facebook platform, your use\n# of this software is subject to the Facebook Developer Principles and\n# Policies [http://developers.facebook.com/policy/]. This copyright notice\n# shall be included in all copies or substantial portions of the software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n# DEALINGS IN THE SOFTWARE\n\nERROR=0\n\nyarn lint --max-warnings 0\n\nif [ $? -ne 0 ]\nthen\n  cat <<EOF\n\n  We've noticed there are lint warnings in the codebase (see above).\n  Please run\n\n    $ yarn lint\n\n  and fix the warnings. Don't forget to amend your changes before pushing!\n\n  HINT:\n    If you see warnings from prettier, you can auto-fix them by\n    running 'yarn lint --fix'.\n\nEOF\n  ERROR=1\nfi\n\nyarn flow\n\nif [ $? -ne 0 ]\nthen\n  cat <<EOF\n\n  We've noticed there are Flow errors in the codebase (see above).\n  Please run\n\n    $ yarn flow\n\n  and fix the errors. Don't forget to amend your changes before pushing!\n\nEOF\n  ERROR=1\nfi\n\nyarn test\n\nif [ $? -ne 0 ]\nthen\n  cat <<EOF\n\n  We've noticed there are test errors in the codebase (see above).\n  Please run\n\n    $ yarn test\n\n  and fix the errors. Don't forget to amend your changes before pushing!\n\nEOF\n  ERROR=1\nfi\n\nexit $ERROR\n"
  },
  {
    "path": "scripts/run-android-app.sh",
    "content": "#!/bin/bash\n\n# Copyright 2016 Facebook, Inc.\n#\n# You are hereby granted a non-exclusive, worldwide, royalty-free license to\n# use, copy, modify, and distribute this software in source code or binary\n# form for use in connection with the web services and APIs provided by\n# Facebook.\n#\n# As with any software that integrates with the Facebook platform, your use\n# of this software is subject to the Facebook Developer Principles and\n# Policies [http://developers.facebook.com/policy/]. This copyright notice\n# shall be included in all copies or substantial portions of the software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n# DEALINGS IN THE SOFTWARE\n\nadb reverse tcp:1337 tcp:1337  # Parse server\nadb reverse tcp:4000 tcp:4000  # GraphQL server\nyarn react-native run-android\n"
  },
  {
    "path": "server/.gitignore",
    "content": "npm-debug.log\n"
  },
  {
    "path": "server/graphql/.babelrc",
    "content": "{\n  \"presets\": [\n    \"es2015\"\n  ]\n}\n"
  },
  {
    "path": "server/graphql/Dockerfile",
    "content": "FROM node:8.4-alpine\n\nADD . .\nRUN [\"npm\", \"install\"]\nRUN [\"npm\", \"run\", \"build\"]\n\nENTRYPOINT [ \"node\", \"lib/index.js\" ]\n"
  },
  {
    "path": "server/graphql/package.json",
    "content": "{\n  \"name\": \"2017-f8-graphql-server\",\n  \"version\": \"1.4.0\",\n  \"description\": \"GraphQL API Server for F8 2017\",\n  \"scripts\": {\n    \"build\": \"babel src --out-dir lib --copy-files\",\n    \"start\": \"PARSE_URL=http://localhost:1337/parse babel-node src/index.js\"\n  },\n  \"dependencies\": {\n    \"express\": \"^4.15.4\",\n    \"express-graphql\": \"0.6.11\",\n    \"graphql\": \"^0.11.3\",\n    \"graphql-relay\": \"0.5.2\",\n    \"parse\": \"^1.9.2\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.24.1\",\n    \"babel-preset-es2015\": \"6.24.1\"\n  }\n}\n"
  },
  {
    "path": "server/graphql/src/index.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport process from \"process\";\nimport express from \"express\";\nimport graphqlHTTP from \"express-graphql\";\nimport Parse from \"parse/node\";\n\nimport schema from \"./schema\";\n\nParse.initialize(\"oss-f8-app-2017\");\nParse.serverURL = process.env.PARSE_URL;\nParse.masterKey = \"oss-f8-app-2017-mk\";\nParse.Cloud.useMasterKey();\n\nconst app = express();\napp.use(\n  \"/graphql\",\n  graphqlHTTP({\n    schema,\n    graphiql: true\n  })\n);\napp.listen(4000);\n"
  },
  {
    "path": "server/graphql/src/schema/__generated__/schema.graphql",
    "content": "type Demo implements Node {\n  # The ID of an object\n  id: ID!\n  title: String\n  description: String\n  booking: String\n  location: String\n  links: [DemoLink]\n  logo: String\n  logoHeight: Int\n  logoWidth: Int\n  devGarage: Boolean\n}\n\ntype DemoLink {\n  title: String\n  url: String\n}\n\n# An object with an ID\ninterface Node {\n  # The id of the object.\n  id: ID!\n}\n\ntype Query {\n  # Fetches an object given its ID\n  node(\n    # The ID of an object\n    id: ID!\n  ): Node\n  demos: [Demo]\n}\n"
  },
  {
    "path": "server/graphql/src/schema/demo.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport {\n  GraphQLBoolean,\n  GraphQLInt,\n  GraphQLList,\n  GraphQLObjectType,\n  GraphQLString\n} from \"graphql\";\nimport { globalIdField } from \"graphql-relay\";\nimport Parse from \"parse/node\";\n\nimport typeRegistry from \"./typeRegistry\";\n\nconst Demo = Parse.Object.extend(\"Demo\");\n\nconst demoLinkType = new GraphQLObjectType({\n  name: \"DemoLink\",\n  fields: () => ({\n    title: {\n      type: GraphQLString,\n      resolve: link => link.title\n    },\n    url: {\n      type: GraphQLString,\n      resolve: link => link.url\n    }\n  })\n});\n\nconst demoType = new GraphQLObjectType({\n  name: \"Demo\",\n  fields: () => ({\n    id: globalIdField(),\n    title: {\n      type: GraphQLString,\n      resolve: demo => demo.get(\"title\")\n    },\n    description: {\n      type: GraphQLString,\n      resolve: demo => demo.get(\"description\")\n    },\n    booking: {\n      type: GraphQLString,\n      resolve: demo => demo.get(\"booking\")\n    },\n    location: {\n      type: GraphQLString,\n      resolve: demo => demo.get(\"location\")\n    },\n    links: {\n      type: new GraphQLList(demoLinkType),\n      resolve: demo => demo.get(\"links\")\n    },\n    logo: {\n      type: GraphQLString,\n      resolve: demo => demo.get(\"logo\") && demo.get(\"logo\").url()\n    },\n    logoHeight: {\n      type: GraphQLInt,\n      resolve: demo => demo.get(\"logoHeight\")\n    },\n    logoWidth: {\n      type: GraphQLInt,\n      resolve: demo => demo.get(\"logoWidth\")\n    },\n    devGarage: {\n      type: GraphQLBoolean,\n      resolve: demo => demo.get(\"devGarage\")\n    }\n  }),\n  interfaces: () => [require(\"./node\").nodeInterface]\n});\ntypeRegistry.register(demoType);\n\nconst demosField = {\n  type: new GraphQLList(demoType),\n  resolve: () => new Parse.Query(Demo).find()\n};\n\nexport { demosField };\n"
  },
  {
    "path": "server/graphql/src/schema/index.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport { GraphQLSchema } from \"graphql\";\n\nimport { queryType } from \"./query\";\n\nexport default new GraphQLSchema({\n  query: queryType\n});\n"
  },
  {
    "path": "server/graphql/src/schema/node.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n *\n * @flow\n */\n\nimport { fromGlobalId, nodeDefinitions } from \"graphql-relay\";\nimport Parse from \"parse/node\";\n\nimport typeRegistry from \"./typeRegistry\";\n\nmodule.exports = nodeDefinitions(\n  globalId => {\n    const { type, id } = fromGlobalId(globalId);\n    return new Parse.Query(type).get(id);\n  },\n  obj => typeRegistry.lookup(obj.className)\n);\n"
  },
  {
    "path": "server/graphql/src/schema/query.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\nimport { GraphQLObjectType } from \"graphql\";\n\nimport { demosField } from \"./demo\";\nimport { nodeField } from \"./node\";\n\nconst queryType = new GraphQLObjectType({\n  name: \"Query\",\n  fields: () => ({\n    node: nodeField,\n    demos: demosField\n  })\n});\n\nexport { queryType };\n"
  },
  {
    "path": "server/graphql/src/schema/typeRegistry.js",
    "content": "/**\n * Copyright 2016 Facebook, Inc.\n *\n * You are hereby granted a non-exclusive, worldwide, royalty-free license to\n * use, copy, modify, and distribute this software in source code or binary\n * form for use in connection with the web services and APIs provided by\n * Facebook.\n *\n * As with any software that integrates with the Facebook platform, your use\n * of this software is subject to the Facebook Developer Principles and\n * Policies [http://developers.facebook.com/policy/]. This copyright notice\n * shall be included in all copies or substantial portions of the software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE\n */\n\nconst typeRegistry = new Map();\n\nexport default {\n  register(type) {\n    typeRegistry.set(type.name, type);\n  },\n  lookup(name) {\n    return typeRegistry.get(name);\n  }\n};\n"
  },
  {
    "path": "server/mongorestore/Dockerfile",
    "content": "FROM mongo:latest\n\nADD data data\n\nENTRYPOINT [ \\\n  \"mongorestore\", \\\n  \"--drop\", \\\n  \"--db\", \"dev\", \\\n  \"--host\", \"mongo\", \\\n  \"data\" \\\n]\n"
  },
  {
    "path": "server/mongorestore/data/Agenda.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Agenda\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Agenda\",\"background\":true},{\"v\":2,\"key\":{\"_id\":1,\"_created_at\":-1},\"name\":\"_id_1__created_at_-1\",\"ns\":\"f8app_initial.Agenda\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1,\"startTime\":1},\"name\":\"_rperm_1_startTime_1\",\"background\":true,\"ns\":\"f8app_initial.Agenda\"},{\"v\":2,\"key\":{\"sessionSlug\":1},\"name\":\"sessionSlug_1\",\"ns\":\"f8app_initial.Agenda\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Attendance.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/Attendance.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Attendance\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"background\":true,\"ns\":\"f8app_initial.Attendance\"}]}"
  },
  {
    "path": "server/mongorestore/data/Demo.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Demo\"}]}"
  },
  {
    "path": "server/mongorestore/data/FAQ.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.FAQ\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.FAQ\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"ns\":\"f8app_initial.FAQ\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Maps.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Maps\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Maps\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"ns\":\"f8app_initial.Maps\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/MeetupSponsors.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.MeetupSponsors\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.MeetupSponsors\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"background\":true,\"ns\":\"f8app_initial.MeetupSponsors\"}]}"
  },
  {
    "path": "server/mongorestore/data/Meetups.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Meetups\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Meetups\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"background\":true,\"ns\":\"f8app_initial.Meetups\"}]}"
  },
  {
    "path": "server/mongorestore/data/Notification.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Notification\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"background\":true,\"ns\":\"f8app_initial.Notification\"},{\"v\":2,\"key\":{\"_rperm\":1,\"_created_at\":-1},\"name\":\"_rperm_1__created_at_-1\",\"ns\":\"f8app_initial.Notification\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Page.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Page\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Page\",\"background\":true},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"ns\":\"f8app_initial.Page\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/PendingNotifications.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.PendingNotifications\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.PendingNotifications\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Policy.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Policy\"}]}"
  },
  {
    "path": "server/mongorestore/data/Speakers.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Speakers\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Speakers\",\"background\":true},{\"v\":2,\"key\":{\"_id\":1,\"_created_at\":-1},\"name\":\"_id_1__created_at_-1\",\"background\":true,\"ns\":\"f8app_initial.Speakers\"},{\"v\":2,\"key\":{\"_rperm\":1},\"name\":\"_rperm_1\",\"ns\":\"f8app_initial.Speakers\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Survey.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Survey\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.Survey\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/SurveyQuestions.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.SurveyQuestions\"}]}"
  },
  {
    "path": "server/mongorestore/data/SurveyResult.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/SurveyResult.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.SurveyResult\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.SurveyResult\",\"background\":true},{\"v\":2,\"key\":{\"_p_user\":1},\"name\":\"_p_user_1\",\"ns\":\"f8app_initial.SurveyResult\",\"background\":true},{\"v\":2,\"key\":{\"rawAnswers\":1,\"_p_user\":1},\"name\":\"rawAnswers_1__p_user_1\",\"ns\":\"f8app_initial.SurveyResult\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/UTestAgenda.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.UTestAgenda\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.UTestAgenda\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/UnmaskedAgenda.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.UnmaskedAgenda\"},{\"v\":2,\"key\":{\"_created_at\":-1},\"name\":\"_created_at_-1\",\"ns\":\"f8app_initial.UnmaskedAgenda\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/Video.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.Video\"}]}"
  },
  {
    "path": "server/mongorestore/data/_Audience.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Audience\"},{\"v\":2,\"unique\":true,\"key\":{\"name\":1},\"name\":\"name_1\",\"ns\":\"f8app_initial._Audience\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/_Cardinality.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Cardinality\"},{\"v\":2,\"unique\":true,\"key\":{\"className\":1,\"column\":1},\"name\":\"className_1_column_1\",\"background\":true,\"ns\":\"f8app_initial._Cardinality\"}]}"
  },
  {
    "path": "server/mongorestore/data/_EventDimension.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._EventDimension\"}]}"
  },
  {
    "path": "server/mongorestore/data/_GlobalConfig.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._GlobalConfig\"}]}"
  },
  {
    "path": "server/mongorestore/data/_Installation.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/_Installation.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Installation\"},{\"v\":2,\"key\":{\"badge\":1,\"_created_at\":1},\"name\":\"badge_1__created_at_1\",\"ns\":\"f8app_initial._Installation\",\"background\":true},{\"v\":2,\"key\":{\"deviceType\":1},\"name\":\"deviceType_1\",\"background\":true,\"ns\":\"f8app_initial._Installation\"},{\"v\":2,\"key\":{\"_p_user\":1,\"badge\":1,\"_created_at\":1},\"name\":\"_p_user_1_badge_1__created_at_1\",\"ns\":\"f8app_initial._Installation\",\"background\":true},{\"v\":2,\"key\":{\"_p_user\":1,\"deviceType\":1},\"name\":\"_p_user_1_deviceType_1\",\"ns\":\"f8app_initial._Installation\",\"background\":true},{\"v\":2,\"key\":{\"installationId\":1},\"name\":\"installationId_1\",\"ns\":\"f8app_initial._Installation\",\"background\":true},{\"v\":2,\"key\":{\"deviceToken\":1,\"installationId\":1},\"name\":\"_deviceToken_by_installationId\",\"background\":true,\"ns\":\"f8app_initial._Installation\"}]}"
  },
  {
    "path": "server/mongorestore/data/_JobStatus.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._JobStatus\"}]}"
  },
  {
    "path": "server/mongorestore/data/_Join:mySchedule:_User.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Join:mySchedule:_User\"},{\"v\":2,\"unique\":true,\"key\":{\"owningId\":1,\"relatedId\":-1},\"name\":\"owningId_1_relatedId_-1\",\"ns\":\"f8app_initial._Join:mySchedule:_User\",\"background\":true},{\"v\":2,\"unique\":true,\"key\":{\"owningId\":1,\"relatedId\":1},\"name\":\"owningId_1_relatedId_1\",\"background\":true,\"ns\":\"f8app_initial._Join:mySchedule:_User\"},{\"v\":2,\"key\":{\"relatedId\":1},\"name\":\"relatedId_1\",\"ns\":\"f8app_initial._Join:mySchedule:_User\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/_PushStatus.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._PushStatus\"}]}"
  },
  {
    "path": "server/mongorestore/data/_QueryToolQuery.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._QueryToolQuery\"}]}"
  },
  {
    "path": "server/mongorestore/data/_Role.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/_Role.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Role\"},{\"v\":2,\"unique\":true,\"key\":{\"name\":1},\"name\":\"name_1\",\"ns\":\"f8app_initial._Role\",\"background\":true,\"sparse\":true}]}"
  },
  {
    "path": "server/mongorestore/data/_SCHEMA.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._SCHEMA\"}]}"
  },
  {
    "path": "server/mongorestore/data/_Session.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/_Session.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._Session\"},{\"v\":2,\"key\":{\"_session_token\":1},\"name\":\"_session_token_1\",\"ns\":\"f8app_initial._Session\",\"background\":true},{\"v\":2,\"key\":{\"_p_user\":1},\"name\":\"_p_user_1\",\"ns\":\"f8app_initial._Session\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/_User.bson",
    "content": ""
  },
  {
    "path": "server/mongorestore/data/_User.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._User\"},{\"v\":2,\"unique\":true,\"key\":{\"username\":1},\"name\":\"username_1\",\"ns\":\"f8app_initial._User\",\"background\":true,\"sparse\":true},{\"v\":2,\"unique\":true,\"key\":{\"email\":1},\"name\":\"email_1\",\"sparse\":true,\"ns\":\"f8app_initial._User\",\"background\":true},{\"v\":2,\"key\":{\"facebook_id\":1,\"name\":1},\"name\":\"facebook_id_1_name_1\",\"ns\":\"f8app_initial._User\",\"background\":true}]}"
  },
  {
    "path": "server/mongorestore/data/_dummy.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial._dummy\"}]}"
  },
  {
    "path": "server/mongorestore/data/objectlabs-system.metadata.json",
    "content": "{\"options\":{},\"indexes\":[{\"v\":2,\"key\":{\"_id\":1},\"name\":\"_id_\",\"ns\":\"f8app_initial.objectlabs-system\"}]}"
  },
  {
    "path": "server/parse-dashboard/Dockerfile",
    "content": "FROM node:8.4-alpine\n\nRUN [\"npm\", \"install\", \"-g\", \"parse-dashboard\"]\n\nADD config.json config.json\n\nENTRYPOINT [ \\\n  \"parse-dashboard\", \\\n  \"--config\", \"config.json\", \\\n  \"--allowInsecureHTTP\" \\\n]\n"
  },
  {
    "path": "server/parse-dashboard/config.json",
    "content": "{\n  \"apps\": [\n    {\n        \"serverURL\": \"http://localhost:1337/parse\",\n        \"appId\": \"oss-f8-app-2017\",\n        \"masterKey\": \"oss-f8-app-2017-mk\",\n        \"appName\": \"F8\"\n    }\n  ],\n  \"users\": [\n    {\n      \"user\": \"admin\",\n      \"pass\": \"admin\"\n    }\n  ]\n}\n"
  },
  {
    "path": "server/parse-server/.babelrc",
    "content": "{\n  \"presets\": [\n    \"es2015\"\n  ]\n}\n"
  },
  {
    "path": "server/parse-server/Dockerfile",
    "content": "FROM node:8.11-alpine\n\nRUN [\"npm\", \"install\", \"-g\", \"parse-server\"]\n\nADD . .\nRUN [ \"npm\", \"install\" ]\nRUN [ \"npm\", \"run\", \"build\" ]\n\nENTRYPOINT [ \"parse-server\", \"config.json\" ]\n"
  },
  {
    "path": "server/parse-server/cloud/auth.js",
    "content": "const tokenTypes = {\n  PushConsole: 1,\n  MessengerBot: 2,\n  SurveyExports: 3\n};\n\nmodule.exports = {\n  type: tokenTypes,\n\n  valid: function(type, value) {\n    switch (type) {\n      case tokenTypes.PushConsole:\n        return value === process.env.PARSE_TOKEN_PUSHCONSOLE;\n      case tokenTypes.MessengerBot:\n        return value === process.env.PARSE_TOKEN_MESSENGERBOT;\n      case tokenTypes.SurveyExports:\n        return value === process.env.PARSE_TOKEN_SURVEYEXPORTS;\n      default:\n        return false;\n    }\n  }\n};\n"
  },
  {
    "path": "server/parse-server/cloud/functions/agenda.js",
    "content": "\"use strict\";\n\n/* global Parse */\n\nimport _ from \"lodash\";\n\nimport moment from \"moment\";\n\nParse.Cloud.define(\"agenda\", function(request, response) {\n  const Agenda = Parse.Object.extend(\"Agenda\");\n  const query = new Parse.Query(Agenda);\n\n  function queryAgenda() {\n    return new Promise(function(resolve, reject) {\n      query.include(\"speakers\");\n      query.exists(\"day\");\n      query.find({ useMasterKey: true }).then(function(results) {\n        const json = results.map(function(result) {\n          return result.toJSON();\n        });\n        buildSchedule(json).then(function(formattedSchedule) {\n          resolve(formattedSchedule);\n        });\n      });\n    });\n  }\n\n  function buildSchedule(schedule) {\n    let dayArrayLookup;\n    const sortedSchedule = [{}, {}];\n\n    return new Promise(function(resolve, reject) {\n      _.forEach(schedule, function(session) {\n        // Go ahead and add new time identifier to each session\n        session.sortTime = moment(session.startTime.iso)\n          .utcOffset(0)\n          .format(\"X\");\n        session.displayTime = moment(session.startTime.iso)\n          .utcOffset(0)\n          .format(\"h:mma\");\n\n        dayArrayLookup = session.day - 1;\n\n        // Check to see if timeblock exists on day\n        if (!sortedSchedule[dayArrayLookup][session.sortTime]) {\n          sortedSchedule[dayArrayLookup][session.sortTime] = [];\n        }\n\n        // Add to appropriate timeblock\n        sortedSchedule[dayArrayLookup][session.sortTime].push(session);\n      });\n\n      resolve(sortedSchedule);\n    }); // Promise\n  } // buildSchedule\n\n  Parse.Promise.when([queryAgenda()]).then(\n    function(results) {\n      response.success(results);\n    },\n    function(error) {\n      response.error(error);\n    }\n  );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/functions/friends.js",
    "content": "\"use strict\";\n/* global Parse */\n\nParse.Cloud.define(\"friends\", function(request, response) {\n  const user = request.user;\n  if (!user) {\n    return response.success([]);\n  }\n  if (!Parse.FacebookUtils.isLinked(user)) {\n    return response.error(\"Current user is not linked to Facebook\");\n  }\n\n  const authData = user.get(\"authData\");\n  const token = authData.facebook.access_token;\n  // TODO: Fetch all friends using paging\n  Parse.Cloud\n    .httpRequest({\n      url:\n        \"https://graph.facebook.com/me/friends?fields=id&access_token=\" + token\n    })\n    .then(function(res) {\n      const friendIds = res.data.data.map(function(friend) {\n        return friend.id;\n      });\n\n      const query = new Parse.Query(Parse.User)\n        .containedIn(\"facebook_id\", friendIds)\n        .ascending(\"name\");\n\n      return query\n        .find({ useMasterKey: true })\n        .then(function(users) {\n          return Parse.Promise.when(users.map(fetchSchedule));\n        })\n        .then(function(friends) {\n          return friends.filter(function(friend) {\n            return friend !== null;\n          });\n        });\n    })\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nfunction fetchSchedule(user) {\n  if (!user.get(\"sharedSchedule\")) {\n    return Parse.Promise.as(null);\n  }\n  // https://www.parse.com/questions/can-i-use-include-in-a-query-to-include-all-members-of-a-parserelation-error-102\n  return user\n    .relation(\"mySchedule\")\n    .query()\n    .find({ useMasterKey: true })\n    .then(function(sessions) {\n      const schedule = {};\n      sessions.forEach(function(session) {\n        schedule[session.id] = true;\n      });\n      return {\n        id: user.get(\"facebook_id\"),\n        name: user.get(\"name\"),\n        link: user.get(\"link\"),\n        schedule: schedule\n      };\n    });\n}\n"
  },
  {
    "path": "server/parse-server/cloud/functions/index.js",
    "content": "// fbf8.com website endpoints\nimport \"./agenda\";\n\nimport \"./meetups\";\n\n// app push/notifications/surveys\nimport \"./notifications\";\n\nimport \"./surveys\";\nimport \"./surveyexports\";\n\n// app facebook friends\nimport \"./friends\";\n\n// fb messenger bot\nimport \"./messengerbot\";\n\n// app push/survey/attendance tests\nimport \"./tests\";\n"
  },
  {
    "path": "server/parse-server/cloud/functions/meetups.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Meetups = Parse.Object.extend(\"Meetups\");\nconst Sponsors = Parse.Object.extend(\"MeetupSponsors\");\n\nParse.Cloud.define(\"meetups\", function(request, response) {\n  const meetupsQuery = new Parse.Query(Meetups);\n  const sponsorsQuery = new Parse.Query(Sponsors);\n\n  function queryMeetups() {\n    return new Promise(function(resolve, reject) {\n      meetupsQuery.find({\n        success: function(results) {\n          resolve(results);\n        },\n        useMasterKey: true\n      });\n    });\n  }\n\n  function querySponsors() {\n    return new Promise(function(resolve, reject) {\n      sponsorsQuery.find({\n        success: function(results) {\n          resolve(results);\n        },\n        useMasterKey: true\n      });\n    });\n  }\n\n  Parse.Promise.when([queryMeetups(), querySponsors()]).then(\n    function(results) {\n      response.success(results);\n    },\n    function(error) {\n      response.error(error);\n    }\n  );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/functions/messengerbot.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Agenda = Parse.Object.extend(\"Agenda\");\n\nimport CloudAuth from \"../auth\";\n\nParse.Cloud.define(\"mbot_get_schedule\", function(request, response) {\n  // require mbot permissions token (instead of master key)\n  if (!CloudAuth.valid(CloudAuth.type.MessengerBot, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (mbot)\");\n  }\n  // necessary for action\n  if (!request.params.facebook_user_id) {\n    return response.error(\"fb user id required\");\n  }\n\n  // just in case passed as number\n  const fbid = String(request.params.facebook_user_id);\n\n  // do query\n  new Parse.Query(Parse.User)\n    .equalTo(\"facebook_id\", fbid)\n    .find({ useMasterKey: true })\n    .then(function(users) {\n      console.log(\"mbot found users\", users);\n      return Parse.Promise.when(users.map(fetchSchedule));\n    })\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nfunction fetchSchedule(user) {\n  // if (!user.get('sharedSchedule')) {\n  //   return Parse.Promise.as(null);\n  // }\n  // https://www.parse.com/questions/can-i-use-include-in-a-query-to-include-all-members-of-a-parserelation-error-102\n  return user\n    .relation(\"mySchedule\")\n    .query()\n    .find({ useMasterKey: true })\n    .then(function(sessions) {\n      const schedule = {};\n      sessions.forEach(function(session) {\n        schedule[session.id] = true;\n      });\n      return {\n        id: user.get(\"facebook_id\"),\n        name: user.get(\"name\"),\n        schedule: schedule\n      };\n    });\n}\n\nParse.Cloud.define(\"mbot_add_to_schedule\", function(request, response) {\n  // require mbot permissions token (instead of master key)\n  if (!CloudAuth.valid(CloudAuth.type.MessengerBot, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (mbot)\");\n  }\n  // necessary for action\n  if (!request.params.facebook_user_id || !request.params.session_id) {\n    return response.error(\"fb user id and session id required\");\n  }\n\n  // just in case passed as number\n  const fbid = String(request.params.facebook_user_id);\n  const sid = String(request.params.session_id);\n\n  // do query\n  new Parse.Query(Parse.User)\n    .equalTo(\"facebook_id\", fbid)\n    .find({ useMasterKey: true })\n    .then(function(users) {\n      return Parse.Promise.when(\n        users.map(function(user) {\n          console.log(\"add to user schedule\", user);\n          user.relation(\"mySchedule\").add(new Agenda({ id: sid }));\n          return user.save(null, { useMasterKey: true });\n          // removed installation channel updates as\n          // it's now handled by afterSave Parse.User\n        })\n      );\n    })\n    .then(\n      function(value) {\n        response.success(true);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"mbot_remove_from_schedule\", function(request, response) {\n  // require mbot permissions token (instead of master key)\n  if (!CloudAuth.valid(CloudAuth.type.MessengerBot, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (mbot)\");\n  }\n  // necessary for action\n  if (!request.params.facebook_user_id || !request.params.session_id) {\n    return response.error(\"fb user id and session id required\");\n  }\n\n  // just in case passed as number\n  const fbid = String(request.params.facebook_user_id);\n  const sid = String(request.params.session_id);\n\n  // do query\n  new Parse.Query(Parse.User)\n    .equalTo(\"facebook_id\", fbid)\n    .find({ useMasterKey: true })\n    .then(function(users) {\n      return Parse.Promise.when(\n        users.map(function(user) {\n          console.log(\"remove from user schedule\", user);\n          user.relation(\"mySchedule\").remove(new Agenda({ id: sid }));\n          return user.save(null, { useMasterKey: true });\n          // removed installation channel updates as\n          // it's now handled by afterSave Parse.User\n        })\n      );\n    })\n    .then(\n      function(value) {\n        response.success(true);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"mbot_users_by_session\", function(request, response) {\n  // require mbot permissions token (instead of master key)\n  if (!CloudAuth.valid(CloudAuth.type.MessengerBot, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (mbot)\");\n  }\n  // necessary for action\n  if (!request.params.session_id) {\n    return response.error(\"session id required\");\n  }\n\n  // just in case passed as number\n  const sid = String(request.params.session_id);\n\n  // do many queries\n  new Parse.Query(Parse.User)\n    .limit(1000) // TODO: paginate/chunk results!\n    .find({ useMasterKey: true })\n    .then(function(users) {\n      return Parse.Promise.when(\n        users.map(function(user) {\n          return hasSavedSession(user, sid);\n        })\n      );\n    })\n    .then(\n      function(value) {\n        const filtered = [];\n        value.forEach(function(u) {\n          if (u !== null) {\n            filtered.push({\n              id: u.id,\n              name: u.get(\"name\"),\n              facebook_id: u.get(\"facebook_id\")\n            });\n          }\n        });\n        response.success(filtered);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nfunction hasSavedSession(user, sid) {\n  // if (!user.get('sharedSchedule')) {\n  //   return Parse.Promise.as(null);\n  // }\n  return user\n    .relation(\"mySchedule\")\n    .query()\n    .find({ useMasterKey: true })\n    .then(function(sessions) {\n      let userSavedSession = false;\n      sessions.forEach(function(session) {\n        if (session.id === sid) {\n          userSavedSession = true;\n        }\n      });\n      return userSavedSession ? user : null;\n    });\n}\n"
  },
  {
    "path": "server/parse-server/cloud/functions/notifications.js",
    "content": "\"use strict\";\n\n/* global Parse */\n\nimport CloudAuth from \"../auth\";\n\nconst Notification = Parse.Object.extend(\"Notification\"); // Done: make real\nconst PendingNotifications = Parse.Object.extend(\"PendingNotifications\");\n\nParse.Cloud.define(\"send_push_by_channel\", function(request, response) {\n  // gate request by cc push token\n  if (!CloudAuth.valid(CloudAuth.type.PushConsole, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (push)\");\n  }\n\n  // data and channels required\n  if (!request.params.data || !request.params.channels) {\n    return response.error(\"Error: missing required params\");\n  }\n\n  const pushData = request.params.data;\n  pushData.badge = \"Increment\";\n  pushData.sound = \"default\";\n\n  Parse.Push\n    .send(\n      {\n        // channels: ['developer'], // DONE: remove scoping\n        channels: request.params.channels, // DONE: remove scoping\n        data: pushData\n      },\n      { useMasterKey: true }\n    )\n    // return a success or error response\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"send_pending_notification\", function(request, response) {\n  // gate request by cc push token\n  if (!CloudAuth.valid(CloudAuth.type.PushConsole, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (push)\");\n  }\n  // require an id in request body\n  if (!request.params.id) {\n    return response.error(\"Error: pending id required\");\n  }\n\n  // get the corresponding PendingNotification based on request's id param\n  new Parse.Query(PendingNotifications)\n    .get(request.params.id, { useMasterKey: true })\n    .then(function(pending) {\n      // Check if anything was found\n      if (!pending) {\n        throw new Error(\"No pending notification found\");\n      }\n\n      // create a new notificatoin based on pending data\n      return new Notification().save(\n        {\n          text: pending.get(\"text\"),\n          url: pending.get(\"url\"),\n          urlTitle: pending.get(\"urlTitle\"),\n          image: pending.get(\"image\")\n        },\n        { useMasterKey: true }\n      );\n    })\n    .then(function(notif) {\n      // after creating the notification object, send to users\n      return Parse.Push.send(\n        {\n          // channels: ['developer'], // TODO: remove scoping\n          where: {}, // TODO: replace dev channel with empty query\n          data: {\n            alert: notif.get(\"text\"),\n            url: notif.get(\"url\"),\n            urlTitle: notif.get(\"urlTitle\"),\n            image: notif.get(\"image\"),\n\n            badge: \"Increment\",\n            sound: \"default\"\n          }\n        },\n        { useMasterKey: true }\n      );\n    })\n    // return a success or error response\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/functions/surveyexports.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Survey = Parse.Object.extend(\"Survey\");\nconst SurveyResult = Parse.Object.extend(\"SurveyResult\");\nconst SurveyQuestions = Parse.Object.extend(\"SurveyQuestions\");\n\nimport CloudAuth from \"../auth\";\n\nParse.Cloud.define(\"survey_response_rate\", function(request, response) {\n  if (!CloudAuth.valid(CloudAuth.type.SurveyExports, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (survey export)\");\n  }\n\n  const allQuery = new Parse.Query(SurveyResult).count({ useMasterKey: true });\n\n  const incompleteQuery = new Parse.Query(SurveyResult)\n    .equalTo(\"rawAnswers\", null)\n    .count({ useMasterKey: true });\n\n  Parse.Promise\n    .when(allQuery, incompleteQuery)\n    .then(function(allCount, incompletCount) {\n      return {\n        total: allCount,\n        incomplete: incompletCount\n      };\n    })\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"export_survey_results_by_questions\", function(\n  request,\n  response\n) {\n  if (!CloudAuth.valid(CloudAuth.type.SurveyExports, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (survey export)\");\n  }\n  if (!request.params.questions_id) {\n    return response.error({ message: \"Need a question set id\" });\n  }\n\n  const paginatedLength = 1000;\n  const sq = new SurveyQuestions();\n  sq.id = request.params.questions_id;\n\n  // before making queries, figure out how many it will take (via count)\n  new Parse.Query(SurveyResult)\n    .notEqualTo(\"rawAnswers\", null) // first, ignore empty ones\n    .matchesQuery(\"survey\", new Parse.Query(Survey).equalTo(\"questions\", sq)) // second, limit by SurveyQuestions\n    .count({ useMasterKey: true }) // count the results\n    .then(function(totalCount) {\n      // create as many queries as it takes to get all relevant results\n      const queries = [];\n      const iterations = Math.ceil(totalCount / paginatedLength);\n      for (let i = 0; i < iterations; i++) {\n        queries.push(\n          new Parse.Query(SurveyResult)\n            .notEqualTo(\"rawAnswers\", null)\n            .matchesQuery(\n              \"survey\",\n              new Parse.Query(Survey).equalTo(\"questions\", sq)\n            )\n            .include(\"survey\")\n            .include(\"survey.session\")\n            .include(\"user\")\n            .limit(paginatedLength)\n            .skip(i * paginatedLength)\n            .find({ useMasterKey: true })\n        );\n      }\n      // return parse promise when all necessary queries have aggregated results\n      return Parse.Promise.when(queries);\n    })\n    .then(function(grouped) {\n      // group the paginated queries into a flat array of SurveyResult-s;\n      const combined = [];\n      grouped.map(function(g) {\n        g.map(function(r) {\n          combined.push({\n            fbid: r.get(\"user\").get(\"facebook_id\"),\n            sessionId: r.get(\"survey\").get(\"session\").id,\n            sessionTitle: r\n              .get(\"survey\")\n              .get(\"session\")\n              .get(\"sessionTitle\"),\n            answers: r.get(\"rawAnswers\")\n          });\n        });\n      });\n      return combined;\n    })\n    // convert raw response data to a usable json format\n    .then(function(allSurveyResults) {\n      // get the original questions and map raw answers to them\n      return new Parse.Query(SurveyQuestions)\n        .get(sq.id, { useMasterKey: true })\n        .then(function(questionKey) {\n          // common result keys\n          const sessionIdKey = \"Session Id\";\n          const sessionTitleKey = \"Session Title\";\n          const fbidKey = \"App-scoped User Id\";\n          const csvKeys = [sessionIdKey, sessionTitleKey, fbidKey]; // append the rest later;\n          // get the raw question data\n          const rawQuestions = questionKey.get(\"questions\");\n          // process the raw data arrays and end up with normalized columns\n          const arrayResults = allSurveyResults.map(function(resultObj) {\n            const surveyResultArr = JSON.parse(resultObj.answers);\n            // create temp result obj\n            const obj = {};\n            // set common columns\n            obj[sessionIdKey] = resultObj.sessionId;\n            obj[sessionTitleKey] = resultObj.sessionTitle;\n            obj[fbidKey] = resultObj.fbid;\n            // set survey-specific columns\n            surveyResultArr.map(function(answerValue, answerIndex) {\n              // mutate temp object with variable amount of columns\n              const question = rawQuestions[answerIndex];\n              // flatten further if question is of a multiple-response type\n              if (question.ratings && question.ratings.rows) {\n                question.ratings.rows.map(function(rowItem, rowIndex) {\n                  const normalizedArrayKey = rowItem + \" - \" + question.text;\n                  obj[normalizedArrayKey] = answerValue[rowIndex];\n                  // add to list of csv cols (once)\n                  if (csvKeys.indexOf(normalizedArrayKey) < 0) {\n                    csvKeys.push(normalizedArrayKey);\n                  }\n                });\n              } else {\n                obj[question.text] = answerValue;\n                // add to list of csv cols (once)\n                if (csvKeys.indexOf(question.text) < 0) {\n                  csvKeys.push(question.text);\n                }\n              }\n            });\n            return obj;\n          });\n          return { fields: csvKeys.join(\",\"), data: arrayResults }; // prepare for json2csv conversion\n        });\n    })\n    // then convert from JSON to CSV format\n    // ... json2csv()\n    // then return\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/functions/surveys.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Agenda = Parse.Object.extend(\"Agenda\");\nconst Attendance = Parse.Object.extend(\"Attendance\");\nconst Survey = Parse.Object.extend(\"Survey\");\nconst SurveyResult = Parse.Object.extend(\"SurveyResult\");\n\nimport CloudAuth from \"../auth\";\n\nParse.Cloud.define(\"send_surveys\", function(request, response) {\n  if (!CloudAuth.valid(CloudAuth.type.PushConsole, request.params._token)) {\n    return response.error(\"CloudAuth: Permission denied (push)\");\n  }\n\n  const sessionId = request.params.sessionId;\n  if (!sessionId) {\n    return response.error(\"Need sessionId\");\n  }\n\n  console.log(\"Fetching attendees for \" + sessionId);\n  const agenda = new Agenda({ id: sessionId });\n  const attendees = new Parse.Query(Attendance)\n    .equalTo(\"agenda\", agenda)\n    .notEqualTo(\"sent\", true)\n    .find({ useMasterKey: true });\n  const survey = new Parse.Query(Survey)\n    .equalTo(\"session\", agenda)\n    .first({ useMasterKey: true });\n\n  Parse.Promise\n    .when(attendees, survey, new Parse.Query(Agenda).get(sessionId))\n    .then(sendSurveys)\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"surveys\", function(request, response) {\n  // Parse.Cloud.useMasterKey();\n\n  const user = request.user;\n  if (!user) {\n    return response.success([]);\n  }\n\n  new Parse.Query(SurveyResult)\n    .equalTo(\"user\", user)\n    .equalTo(\"rawAnswers\", null)\n    .include(\"survey\")\n    .include(\"survey.session\")\n    .include(\"survey.questions\")\n    .find({ useMasterKey: true })\n    .then(toSurveys)\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"submit_survey\", function(request, response) {\n  // Parse.Cloud.useMasterKey();\n\n  const user = request.user;\n  if (!user) {\n    return response.error({ message: \"Not logged in\" });\n  }\n\n  const params = request.params;\n  if (!params.id || !params.answers) {\n    return response.error({ message: \"Need id and answers\" });\n  }\n\n  new Parse.Query(SurveyResult)\n    .equalTo(\"user\", user)\n    .equalTo(\"objectId\", params.id)\n    .find({ useMasterKey: true })\n    .then(function(results) {\n      if (results.length === 0) {\n        throw new Error(\"No user/id combination found\");\n      }\n      return results[0].save(\n        {\n          // a1: params.answers[0],\n          // a2: params.answers[1],\n          rawAnswers: JSON.stringify(params.answers)\n        },\n        { useMasterKey: true }\n      );\n    })\n    .then(\n      function(value) {\n        response.success(value);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nfunction sendSurveys(attendees, survey, session) {\n  if (!survey) {\n    throw new Error(\"Survey not found for session \" + session.id);\n  }\n\n  console.log(\"Found \" + attendees.length + \" attendees\");\n  return Parse.Promise\n    .when(\n      attendees.map(function(record) {\n        const user = record.get(\"user\");\n        return new SurveyResult()\n          .save(\n            {\n              user: user,\n              survey: survey\n            },\n            { useMasterKey: true }\n          )\n          .then(function() {\n            return Parse.Push.send(\n              {\n                where: new Parse.Query(Parse.Installation).equalTo(\n                  \"user\",\n                  user\n                ),\n                data: {\n                  badge: \"Increment\",\n                  alert: 'How was \"' + session.get(\"sessionTitle\") + '\"?',\n                  e: true, // ephemeral\n                  sound: \"default\"\n                }\n              },\n              { useMasterKey: true }\n            );\n          })\n          .then(function() {\n            return record.save({ sent: true });\n          });\n      })\n    )\n    .then(function() {\n      return arguments.length;\n    });\n}\n\nfunction toSurveys(emptyResults) {\n  return emptyResults.map(function(emptyResult) {\n    const survey = emptyResult.get(\"survey\");\n    return {\n      id: emptyResult.id,\n      sessionId: survey.get(\"session\").id,\n      description: survey.get(\"description\"),\n      questions: survey.get(\"questions\").get(\"questions\"),\n      time: emptyResult.createdAt.getTime()\n    };\n  });\n}\n"
  },
  {
    "path": "server/parse-server/cloud/functions/tests.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Survey = Parse.Object.extend(\"Survey\");\nconst SurveyResult = Parse.Object.extend(\"SurveyResult\");\n\nParse.Cloud.define(\"test_push\", function(request, response) {\n  // Parse.Cloud.useMasterKey();\n\n  const user = request.user;\n  if (!user) {\n    return response.error({ message: \"Not logged in\" });\n  }\n\n  const query = new Parse.Query(Parse.Installation);\n  query.equalTo(\"user\", user);\n\n  const userName = user.get(\"name\").split(\" \")[0];\n  let data;\n  if (request.params.url === \"link\") {\n    data = {\n      alert: \"Hey \" + userName + \", check out this great website\",\n      url: \"https://www.fbf8.com/\",\n      urlTitle: \"Facebook Developer Conference\"\n    };\n  } else if (request.params.url === \"image\") {\n    data = {\n      alert: \"Hey \" + userName + \", F8 shared a photo!\",\n      image:\n        \"https://scontent-lga3-1.xx.fbcdn.net/v/t31.0-0/q88/c239.0.1198.630/s526x296/14480488_10154024657733553_686849147673384434_o.jpg?oh=57be4988e732a1df38b86356888a7138&oe=590EDE1D\"\n    };\n  } else if (request.params.url === \"video\") {\n    data = {\n      alert: \"Hey \" + userName + \", F8 shared a video!\",\n      urlTitle: \"Watch 2016 highlights\",\n      url: \"https://developers.facebook.com/videos/f8-2016/f8-2016-highlights/\",\n      image:\n        \"https://scontent-lga3-1.xx.fbcdn.net/v/t31.0-0/q88/c239.0.1198.630/s526x296/14480488_10154024657733553_686849147673384434_o.jpg?oh=57be4988e732a1df38b86356888a7138&oe=590EDE1D\"\n    };\n  } else if (request.params.url === \"session\") {\n    data = {\n      alert:\n        userName +\n        ', \"The Evolution of React and GraphQL at Facebook and Beyond\" is about to begin',\n      url: \"f8://KvqMttRNBG\"\n    };\n  } else {\n    data = {\n      alert: \"Test notification for \" + userName\n    };\n  }\n\n  data.badge = \"Increment\";\n\n  Parse.Push\n    .send(\n      {\n        where: query,\n        push_time: new Date(Date.now() + 3000),\n        badge: \"Increment\",\n        data: data\n      },\n      { useMasterKey: true }\n    )\n    .then(\n      function() {\n        response.success([]);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nParse.Cloud.define(\"test_survey\", function(request, response) {\n  // Parse.Cloud.useMasterKey();\n\n  const user = request.user;\n  if (!user) {\n    return response.error({ message: \"Not logged in\" });\n  }\n\n  new Parse.Query(Survey)\n    .include([\"session\", \"questions\"])\n    .find({ useMasterKey: true })\n    .then(pickRandom)\n    .then(function(survey) {\n      console.log(survey);\n      const sessionTitle = survey.get(\"session\").get(\"sessionTitle\");\n      return new SurveyResult()\n        .save({ user: user, survey: survey }, { useMasterKey: true })\n        .then(function() {\n          return Parse.Push.send(\n            {\n              where: new Parse.Query(Parse.Installation).equalTo(\"user\", user),\n              push_time: new Date(Date.now() + 3000),\n              data: {\n                badge: \"Increment\",\n                alert: 'How did \"' + sessionTitle + '\" go?',\n                e: true // ephemeral\n              }\n            },\n            { useMasterKey: true }\n          );\n        });\n    })\n    .then(\n      function() {\n        response.success([]);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n\nfunction pickRandom(list) {\n  if (list.length === 0) {\n    throw new Error(\"Can not pick random item from empty list\");\n  }\n  const index = Math.floor(Math.random() * list.length);\n  return list[index];\n}\n\nParse.Cloud.define(\"test_attendance\", function(request, response) {\n  const Agenda = Parse.Object.extend(\"Agenda\");\n  new Parse.Query(Agenda)\n    .select([\"id\", \"sessionTitle\"])\n    .find({ useMasterKey: true })\n    .then(function(agendas) {\n      return Parse.Promise.when(\n        agendas.map(function(agenda) {\n          return new Parse.Query(Parse.User)\n            .equalTo(\"attendance\", agenda)\n            .find(function(users) {\n              console.log(\n                \"Users attending \" +\n                  agenda.get(\"sessionTitle\") +\n                  \": \" +\n                  users.length\n              );\n            });\n        })\n      );\n    })\n    .then(\n      function() {\n        response.success([]);\n      },\n      function(error) {\n        response.error(error);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/index.js",
    "content": "\"use strict\";\n/* global Parse */\n\n// Cloud code status check\nParse.Cloud.define(\"cloudcode_status\", function(request, response) {\n  response.success(\"👌\");\n});\n\n// Cloud functions\nimport \"./functions\";\n\n// Parse Jobs\nimport \"./jobs\";\n\n// Class triggers (beforeSave & afterSave)\nimport \"./triggers\";\n"
  },
  {
    "path": "server/parse-server/cloud/jobs/index.js",
    "content": "// cloud jobs\n// require('./installationSync');\nimport \"./updateVideoSources\";\n\nimport \"./unmaskDemos\";\nimport \"./unmaskAgenda\";\n"
  },
  {
    "path": "server/parse-server/cloud/jobs/installationSync.js",
    "content": "\"use strict\";\n/* global Parse */\n\nParse.Cloud.job(\"installationSync\", function(request, status) {\n  // Parse.Cloud.useMasterKey();\n  let counter = 0;\n\n  new Parse.Query(Parse.Installation)\n    .get({ useMasterKey: true })\n    .each(function(installation) {\n      return findUserChannels(installation.get(\"user\")).then(function(\n        channels\n      ) {\n        counter++;\n        if (counter % 10 === 0) {\n          status.message(counter + \" installations processed.\");\n        }\n        installation.set(\"channels\", channels);\n        return installation.save(null, { useMasterKey: true });\n      });\n    })\n    .then(\n      function() {\n        status.success(\"Sync finished successfully\");\n      },\n      function(error) {\n        console.error(error);\n        status.error(\"Error! \" + error.message);\n      }\n    );\n});\n\nfunction findUserChannels(user) {\n  if (!user) {\n    return Parse.Promise.as([]);\n  }\n\n  return user\n    .relation(\"mySchedule\")\n    .query()\n    .select([\"id\"])\n    .find({ useMasterKey: true })\n    .then(function(schedule) {\n      return schedule.map(function(s) {\n        return \"session_\" + s.id;\n      });\n    });\n}\n"
  },
  {
    "path": "server/parse-server/cloud/jobs/unmaskAgenda.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Agenda = Parse.Object.extend(\"Agenda\"),\n  UnmaskedAgenda = Parse.Object.extend(\"UnmaskedAgenda\");\n\nParse.Cloud.job(\"unmaskAgenda\", function(request, status) {\n  let countTotal = 0,\n    countUpdated = 0;\n  // here we go\n  new Parse.Query(UnmaskedAgenda)\n    .limit(1000)\n    .equalTo(\"unlocked\", true)\n    .find({ useMasterKey: true })\n    .then(function(sessions) {\n      return Parse.Promise.when(\n        sessions.map(function(unmasked) {\n          // get the total length for status result message\n          if (countTotal !== sessions.length) {\n            countTotal = sessions.length;\n          }\n          // find the matching session to update\n          return new Parse.Query(Agenda)\n            .get(unmasked.get(\"agenda\").id, { useMasterKey: true })\n            .then(function(masked) {\n              // Throw if we got here despite an invalid result\n              if (!masked) {\n                throw new Error(\"No matching Agenda item to update!\");\n              }\n              // ...or proceed to updating the fields\n              const updates = {};\n              if (unmasked.get(\"sessionSlug\")) {\n                updates.sessionSlug = unmasked.get(\"sessionSlug\");\n              }\n              if (unmasked.get(\"sessionTitle\")) {\n                updates.sessionTitle = unmasked.get(\"sessionTitle\");\n              }\n              if (unmasked.get(\"sessionDescription\")) {\n                updates.sessionDescription = unmasked.get(\"sessionDescription\");\n              }\n              if (unmasked.get(\"ogImage\")) {\n                updates.ogImage = unmasked.get(\"ogImage\");\n              }\n              if (unmasked.get(\"day\")) {\n                updates.day = unmasked.get(\"day\");\n              }\n              if (unmasked.get(\"speakers\")) {\n                updates.speakers = unmasked.get(\"speakers\");\n              }\n              if (unmasked.get(\"tags\")) {\n                updates.tags = unmasked.get(\"tags\");\n              }\n              // update the Agenda item with unmasked data, or throw if it there's nothing\n              const updatedFields = Object.keys(updates);\n              if (updatedFields.length) {\n                return masked.save(updates, { useMasterKey: true });\n              } else {\n                throw new Error(\"No new data!\");\n              }\n            })\n            .then(\n              function(value) {\n                countUpdated++;\n                return value;\n              },\n              function(error) {\n                console.log(\"Error / unmaskAgenda:\", error);\n                return error;\n              }\n            );\n        })\n      );\n    })\n    .then(\n      function() {\n        status.success(\n          \"Success! Unmasked \" +\n            countUpdated +\n            \" of \" +\n            countTotal +\n            \" sessions\"\n        );\n      },\n      function(error) {\n        console.log(\"Error / unmaskAgenda:\", error);\n        status.error(\"Error! \" + error.message);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/jobs/unmaskDemos.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Demo = Parse.Object.extend(\"Demo\"),\n  UnmaskedDemo = Parse.Object.extend(\"UnmaskedDemo\");\n\nParse.Cloud.job(\"unmaskDemo\", function(request, status) {\n  let countTotal = 0,\n    countUpdated = 0; // for job status feedback\n\n  new Parse.Query(UnmaskedDemo)\n    .limit(1000)\n    .equalTo(\"unlocked\", true)\n    .find({ useMasterKey: true })\n    .then(function(demos) {\n      return Parse.Promise.when(\n        demos.map(function(unmasked) {\n          // get the total length for status result message\n          if (countTotal !== demos.length) {\n            countTotal = demos.length;\n          }\n          // find the matching demo to update\n          return new Parse.Query(Demo)\n            .get(unmasked.get(\"demo\").id, { useMasterKey: true })\n            .then(function(masked) {\n              // Throw if we got here despite an invalid result\n              if (!masked) {\n                throw new Error(\"No matching Demo item to update!\");\n              }\n              // ...or proceed to updating the fields\n              const updates = {};\n              if (unmasked.get(\"title\")) {\n                updates.title = unmasked.get(\"title\");\n              }\n              if (unmasked.get(\"description\")) {\n                updates.description = unmasked.get(\"description\");\n              }\n              if (unmasked.get(\"links\")) {\n                updates.links = unmasked.get(\"links\");\n              }\n              if (unmasked.get(\"location\")) {\n                updates.location = unmasked.get(\"location\");\n              }\n              // update the Demo item with unmasked data, or throw if it there's nothing\n              const updatedFields = Object.keys(updates);\n              if (updatedFields.length) {\n                return masked.save(updates, { useMasterKey: true });\n              } else {\n                throw new Error(\"No new data!\");\n              }\n            })\n            .then(\n              function(value) {\n                countUpdated++;\n                return value;\n              },\n              function(error) {\n                console.log(\"Error / unmaskDemo:\", error);\n                return error;\n              }\n            );\n        })\n      );\n    })\n    .then(\n      function() {\n        status.success(\n          \"Success! Unmasked \" + countUpdated + \" of \" + countTotal + \" demos\"\n        );\n      },\n      function(error) {\n        console.log(\"Error / unmaskDemo:\", error);\n        status.error(\"Error! \" + error.message);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/jobs/updateVideoSources.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Video = Parse.Object.extend(\"Video\"),\n  GRAPH_BASE_URL = \"https://graph.facebook.com/v2.8/\",\n  GRAPH_FIELDS_PARAM = \"?fields=source\", //,picture -> poor quality\n  GRAPH_ACCESS_PARAM = \"&access_token=\" + process.env.FB_GRAPH_ACCESS_TOKEN;\n\nParse.Cloud.job(\"updateVideoSources\", function(request, status) {\n  let countTotal = \"?\";\n  let countUpdated = 0;\n\n  new Parse.Query(Video)\n    .find({ useMasterKey: true })\n    .then(function(videos) {\n      return Parse.Promise.when(\n        videos.map(function(video) {\n          const fbid = video.get(\"facebookId\");\n          if (countTotal !== videos.length) {\n            countTotal = videos.length;\n          }\n          return Parse.Cloud\n            .httpRequest({\n              url:\n                GRAPH_BASE_URL + fbid + GRAPH_FIELDS_PARAM + GRAPH_ACCESS_PARAM\n            })\n            .then(\n              function(response) {\n                const data = response.data;\n                let changed = false;\n                // update the video source, if necessary\n                if (data.source && data.source !== video.get(\"source\")) {\n                  video.set(\"source\", data.source);\n                  changed = true;\n                }\n                // update cover image, if necessary // BAD QUALITY\n                // if(data.picture && data.picture !== video.get('image')){\n                //   video.set('image', data.picture);\n                //   changed = true;\n                // }\n                // save changes\n                if (changed) {\n                  countUpdated++;\n                }\n                return video.save(null, { useMasterKey: true });\n              },\n              function(error) {\n                status.error(error);\n              }\n            );\n        })\n      );\n    })\n    .then(\n      function() {\n        status.success(\n          \"Success! Updated \" + countUpdated + \" of \" + countTotal + \" videos\"\n        );\n      },\n      function(error) {\n        console.error(error);\n        status.error(\"Error! \" + error.message);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/cloud/triggers/index.js",
    "content": "// before & afterSave trigger functions\n// require('./pushChannels'); // legacy, works well but very resource intensive\nimport \"./videos\";\n"
  },
  {
    "path": "server/parse-server/cloud/triggers/pushChannels.js",
    "content": "\"use strict\";\n/* global Parse */\n\nParse.Cloud.afterSave(Parse.User, function(request) {\n  //   Parse.Cloud.useMasterKey();\n  const installationsQ = new Parse.Query(Parse.Installation)\n    .equalTo(\"user\", request.object)\n    .find({ useMasterKey: true });\n  const scheduleQ = request.object\n    .relation(\"mySchedule\")\n    .query()\n    .select([\"id\"])\n    .find({ useMasterKey: true });\n  Parse.Promise\n    .when(installationsQ, scheduleQ)\n    .then(function(installations, schedule) {\n      console.log(\n        \"Found \" +\n          installations.length +\n          \" installations and \" +\n          schedule.length +\n          \" sessions in schedule\"\n      );\n      const sessionIds = schedule.map(function(s) {\n        return \"session_\" + s.id;\n      });\n      console.log(sessionIds);\n      return Parse.Promise.when(\n        installations.map(function(installation) {\n          installation.set(\"channels\", sessionIds);\n          return installation.save(null, { useMasterKey: true });\n        })\n      );\n    })\n    .then(\n      function() {\n        console.log(\"Updated \" + arguments.length + \" installations\");\n      },\n      function(errors) {\n        if (Array.isArray(errors)) {\n          for (let i = 0; i < errors.length; i++) {\n            console.error(\n              \"Error! #\" + i + \" \" + errors[i].message || errors[i]\n            );\n          }\n        } else {\n          console.error(\"Error! \" + errors.message || errors);\n        }\n      }\n    );\n});\n\nParse.Cloud.beforeSave(Parse.Installation, function(request, response) {\n  const installation = request.object;\n  const user = installation.get(\"user\");\n  if (installation.dirtyKeys().indexOf(\"user\") === -1) {\n    response.success();\n    return;\n  }\n\n  if (user) {\n    console.log(\"Set user, will fetch sessions\");\n    user\n      .relation(\"mySchedule\")\n      .query()\n      .select([\"id\"])\n      .find({ useMasterKey: true })\n      .then(function(schedule) {\n        const sessionIds = schedule.map(function(s) {\n          return \"session_\" + s.id;\n        });\n        installation.set(\"channels\", sessionIds);\n        console.log(\n          \"Updated channels to \" + installation.get(\"channels\").join(\", \")\n        );\n        response.success();\n      });\n  } else {\n    console.log(\"No user, will empty channels\");\n    installation.set(\"channels\", []);\n    response.success();\n  }\n});\n"
  },
  {
    "path": "server/parse-server/cloud/triggers/videos.js",
    "content": "\"use strict\";\n/* global Parse */\n\nconst Video = Parse.Object.extend(\"Video\");\n\nParse.Cloud.afterSave(Video, function(request) {\n  if (request.object.existed()) {\n    // skip this if it's an update to an existing object\n    return;\n  }\n  // request up-to-date video source url from Graph API\n  // see ../jobs/updateVideoSources.js\n  return Parse.Cloud\n    .httpRequest({\n      method: \"POST\",\n      url: process.env.PARSE_SERVER_URL + \"/jobs/updateVideoSources\",\n      headers: {\n        \"X-Parse-Application-Id\": process.env.PARSE_APP_ID,\n        \"X-Parse-Master-Key\": process.env.PARSE_MASTER_KEY\n      }\n    })\n    .then(\n      function(value) {\n        console.log(\"Video.afterSave success!\", value);\n      },\n      function(error) {\n        console.log(\"Video.afterSave error!\", error);\n      }\n    );\n});\n"
  },
  {
    "path": "server/parse-server/config.json",
    "content": "{\n  \"appId\": \"oss-f8-app-2017\",\n  \"masterKey\": \"oss-f8-app-2017-mk\",\n  \"databaseURI\": \"mongodb://mongo/dev\",\n  \"cloud\": \"cloud-lib/index.js\"\n}\n"
  },
  {
    "path": "server/parse-server/package.json",
    "content": "{\n  \"name\": \"2017-f8-parse-server\",\n  \"version\": \"1.4.0\",\n  \"description\": \"Parse API Server for F8 2017\",\n  \"scripts\": {\n    \"build\": \"babel cloud --out-dir cloud-lib --copy-files\"\n  },\n  \"dependencies\": {\n    \"lodash\": \"^4.17.13\",\n    \"moment\": \"^2.19.3\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"^6.24.1\",\n    \"babel-preset-es2015\": \"6.24.1\"\n  }\n}\n"
  }
]