Repository: fbsamples/f8app Branch: main Commit: 119f062943c9 Files: 331 Total size: 800.1 KB Directory structure: gitextract_xjzujt5k/ ├── .babelrc ├── .buckconfig ├── .circleci/ │ └── config.yml ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __mocks__/ │ └── react-native.js ├── android/ │ ├── app/ │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── debug/ │ │ │ └── res/ │ │ │ └── values/ │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── facebook/ │ │ │ └── f8/ │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res/ │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores/ │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app.json ├── docker-compose.yml ├── index.android.js ├── index.ios.js ├── ios/ │ ├── F82017/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj/ │ │ │ └── LaunchScreen.xib │ │ ├── F82017.entitlements │ │ ├── Images.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ ├── F82017-tvOS/ │ │ └── Info.plist │ ├── F82017-tvOSTests/ │ │ └── Info.plist │ ├── F82017.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── F82017-tvOS.xcscheme │ │ └── F82017.xcscheme │ └── F82017Tests/ │ ├── F82017Tests.m │ └── Info.plist ├── js/ │ ├── F8Analytics.js │ ├── F8App.js │ ├── F8Navigator.js │ ├── FacebookSDK.js │ ├── Playground.js │ ├── PushNotificationsController.js │ ├── actions/ │ │ ├── config.js │ │ ├── filter.js │ │ ├── index.js │ │ ├── installation.js │ │ ├── login.js │ │ ├── navigation.js │ │ ├── notifications.js │ │ ├── parse.js │ │ ├── schedule.js │ │ ├── surveys.js │ │ ├── test.js │ │ ├── types.js │ │ └── video.js │ ├── common/ │ │ ├── ActionsOverlay.js │ │ ├── Carousel.js │ │ ├── F8ActionSheet.js │ │ ├── F8BackgroundRepeat.js │ │ ├── F8Button.js │ │ ├── F8Colors.js │ │ ├── F8DrawerLayout.js │ │ ├── F8Fonts.js │ │ ├── F8Header.js │ │ ├── F8Linking.js │ │ ├── F8Modal.js │ │ ├── F8PageControl.js │ │ ├── F8ScrollingHeader.js │ │ ├── F8SegmentedControl.js │ │ ├── F8StyleSheet.js │ │ ├── F8Text.js │ │ ├── F8TimelineBackground.js │ │ ├── F8TimelineSegment.js │ │ ├── F8Toast.js │ │ ├── F8Tooltip.js │ │ ├── F8Touchable.js │ │ ├── F8WebView.js │ │ ├── Hitbox.js │ │ ├── ItemsWithSeparator.js │ │ ├── LaunchScreen.js │ │ ├── ListContainer.js │ │ ├── LoginButton.js │ │ ├── MapView.js │ │ ├── MessengerChatHead.js │ │ ├── MessengerModal.js │ │ ├── ParallaxBackground.js │ │ ├── PlayButton.js │ │ ├── ProfilePicture.js │ │ ├── PureListView.js │ │ ├── ViewPager.js │ │ ├── __tests__/ │ │ │ └── convertTimes-test.js │ │ └── convertTimes.js │ ├── env.js │ ├── filter/ │ │ ├── FilterScreen.android.js │ │ ├── FilterScreen.ios.js │ │ ├── FriendsList.js │ │ ├── Header.js │ │ ├── Section.js │ │ ├── TopicItem.js │ │ └── topicIcons.js │ ├── flow-lib.js │ ├── login/ │ │ ├── LoginModal.js │ │ └── LoginScreen.js │ ├── rating/ │ │ ├── Header.js │ │ ├── RadioButtons.js │ │ ├── RatingQuestion.js │ │ ├── RatingScreen.js │ │ ├── StarRating.js │ │ ├── StarRatings.js │ │ └── WrittenResponse.js │ ├── reducers/ │ │ ├── __mocks__/ │ │ │ └── parse.js │ │ ├── __tests__/ │ │ │ ├── maps-test.js │ │ │ ├── notifications-test.js │ │ │ └── schedule-test.js │ │ ├── config.js │ │ ├── createParseReducer.js │ │ ├── faqs.js │ │ ├── friendsSchedules.js │ │ ├── index.js │ │ ├── maps.js │ │ ├── navigation.js │ │ ├── notifications.js │ │ ├── pages.js │ │ ├── policies.js │ │ ├── schedule.js │ │ ├── scheduleFilter.js │ │ ├── scheduleTopics.js │ │ ├── sessions.js │ │ ├── surveys.js │ │ ├── testEventDates.js │ │ ├── user.js │ │ ├── videoFilter.js │ │ ├── videoTopics.js │ │ └── videos.js │ ├── relay-environment.js │ ├── setup.js │ ├── store/ │ │ ├── analytics.js │ │ ├── array.js │ │ ├── compatibility.js │ │ ├── configureStore.js │ │ ├── promise.js │ │ └── track.js │ ├── tabs/ │ │ ├── F8TabsView.js │ │ ├── MenuItem.js │ │ ├── demos/ │ │ │ ├── DemosCarousel.js │ │ │ ├── F8DemoDetails.js │ │ │ ├── F8DemosView.js │ │ │ └── __generated__/ │ │ │ └── F8DemosViewQuery.graphql.js │ │ ├── info/ │ │ │ ├── AboutLocation.js │ │ │ ├── CommonQuestions.js │ │ │ ├── DirectionsLink.js │ │ │ ├── F8AboutView.js │ │ │ ├── F8InfoView.js │ │ │ ├── LinksList.js │ │ │ ├── Section.js │ │ │ └── WiFiDetails.js │ │ ├── maps/ │ │ │ ├── F8MapView.js │ │ │ ├── F8VenueMap.js │ │ │ └── ZoomableImage.js │ │ ├── notifications/ │ │ │ ├── F8NotificationsView.js │ │ │ ├── NotificationCell.js │ │ │ ├── PushNUXModal.js │ │ │ ├── RateSessionsCell.js │ │ │ ├── allNotifications.js │ │ │ ├── findSessionByURI.js │ │ │ └── unseenNotificationsCount.js │ │ ├── schedule/ │ │ │ ├── AddToScheduleButton.js │ │ │ ├── EmptySchedule.js │ │ │ ├── F8FriendGoing.js │ │ │ ├── F8GanttGrid.js │ │ │ ├── F8GanttNowMarker.js │ │ │ ├── F8GanttRow.js │ │ │ ├── F8ScheduleGantt.js │ │ │ ├── F8SessionCell.js │ │ │ ├── F8SessionDetails.js │ │ │ ├── F8SpeakerProfile.js │ │ │ ├── FilterHeader.js │ │ │ ├── FriendCell.js │ │ │ ├── FriendsListView.js │ │ │ ├── FriendsScheduleView.js │ │ │ ├── FriendsUsingApp.js │ │ │ ├── GeneralScheduleView.js │ │ │ ├── HideCompleted.js │ │ │ ├── InviteFriendsButton.js │ │ │ ├── MyScheduleView.js │ │ │ ├── PrivacyIcon.js │ │ │ ├── ProfileButton.js │ │ │ ├── ScheduleListView.js │ │ │ ├── SessionsCarousel.js │ │ │ ├── SessionsSectionHeader.js │ │ │ ├── SharingSettingsCommon.js │ │ │ ├── SharingSettingsModal.js │ │ │ ├── SharingSettingsScreen.js │ │ │ ├── __tests__/ │ │ │ │ ├── formatDuration-test.js │ │ │ │ └── formatTime-test.js │ │ │ ├── filterSessions.js │ │ │ ├── formatDuration.js │ │ │ ├── formatTime.js │ │ │ └── groupSessions.js │ │ └── videos/ │ │ ├── F8EmptyVideosView.js │ │ ├── F8VideoThumb.js │ │ ├── F8VideoView.js │ │ ├── F8VideosView.js │ │ └── filterVideos.js │ └── video/ │ ├── F8VideoPlayer.js │ ├── VideoControls.js │ └── VideoLoader.js ├── package.json ├── scripts/ │ ├── generate-graphql-schema.js │ ├── open-ios-project.js │ ├── optimize-images.sh │ ├── pre-push-checks.sh │ └── run-android-app.sh └── server/ ├── .gitignore ├── graphql/ │ ├── .babelrc │ ├── Dockerfile │ ├── package.json │ └── src/ │ ├── index.js │ └── schema/ │ ├── __generated__/ │ │ └── schema.graphql │ ├── demo.js │ ├── index.js │ ├── node.js │ ├── query.js │ └── typeRegistry.js ├── mongorestore/ │ ├── Dockerfile │ └── data/ │ ├── Agenda.bson │ ├── Agenda.metadata.json │ ├── Attendance.bson │ ├── Attendance.metadata.json │ ├── Demo.bson │ ├── Demo.metadata.json │ ├── FAQ.bson │ ├── FAQ.metadata.json │ ├── Maps.bson │ ├── Maps.metadata.json │ ├── MeetupSponsors.bson │ ├── MeetupSponsors.metadata.json │ ├── Meetups.bson │ ├── Meetups.metadata.json │ ├── Notification.bson │ ├── Notification.metadata.json │ ├── Page.bson │ ├── Page.metadata.json │ ├── PendingNotifications.bson │ ├── PendingNotifications.metadata.json │ ├── Policy.bson │ ├── Policy.metadata.json │ ├── Speakers.bson │ ├── Speakers.metadata.json │ ├── Survey.bson │ ├── Survey.metadata.json │ ├── SurveyQuestions.bson │ ├── SurveyQuestions.metadata.json │ ├── SurveyResult.bson │ ├── SurveyResult.metadata.json │ ├── UTestAgenda.bson │ ├── UTestAgenda.metadata.json │ ├── UnmaskedAgenda.bson │ ├── UnmaskedAgenda.metadata.json │ ├── Video.bson │ ├── Video.metadata.json │ ├── _Audience.bson │ ├── _Audience.metadata.json │ ├── _Cardinality.bson │ ├── _Cardinality.metadata.json │ ├── _EventDimension.bson │ ├── _EventDimension.metadata.json │ ├── _GlobalConfig.bson │ ├── _GlobalConfig.metadata.json │ ├── _Installation.bson │ ├── _Installation.metadata.json │ ├── _JobStatus.bson │ ├── _JobStatus.metadata.json │ ├── _Join:mySchedule:_User.bson │ ├── _Join:mySchedule:_User.metadata.json │ ├── _PushStatus.bson │ ├── _PushStatus.metadata.json │ ├── _QueryToolQuery.bson │ ├── _QueryToolQuery.metadata.json │ ├── _Role.bson │ ├── _Role.metadata.json │ ├── _SCHEMA.bson │ ├── _SCHEMA.metadata.json │ ├── _Session.bson │ ├── _Session.metadata.json │ ├── _User.bson │ ├── _User.metadata.json │ ├── _dummy.bson │ ├── _dummy.metadata.json │ ├── objectlabs-system.bson │ └── objectlabs-system.metadata.json ├── parse-dashboard/ │ ├── Dockerfile │ └── config.json └── parse-server/ ├── .babelrc ├── Dockerfile ├── cloud/ │ ├── auth.js │ ├── functions/ │ │ ├── agenda.js │ │ ├── friends.js │ │ ├── index.js │ │ ├── meetups.js │ │ ├── messengerbot.js │ │ ├── notifications.js │ │ ├── surveyexports.js │ │ ├── surveys.js │ │ └── tests.js │ ├── index.js │ ├── jobs/ │ │ ├── index.js │ │ ├── installationSync.js │ │ ├── unmaskAgenda.js │ │ ├── unmaskDemos.js │ │ └── updateVideoSources.js │ └── triggers/ │ ├── index.js │ ├── pushChannels.js │ └── videos.js ├── config.json └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "plugins": ["relay"], "presets": ["react-native"] } ================================================ FILE: .buckconfig ================================================ [android] target = Google Inc.:Google APIs:23 [maven_repositories] central = https://repo1.maven.org/maven2 ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: working_directory: ~/f8app docker: - image: circleci/node:7.10 steps: - checkout - restore_cache: keys: - v1-dependencies-{{ checksum "package.json" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: yarn install - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} - run: yarn lint - run: yarn flow - run: yarn test android: working_directory: ~/f8app docker: - image: circleci/android:api-27-node8-alpha steps: - checkout - run: yarn install - run: cd android && ./gradlew assembleDebug workflows: version: 2 build_and_test: jobs: - build - android ================================================ FILE: .eslintignore ================================================ **/node_modules/** ================================================ FILE: .eslintrc ================================================ { "parser": "babel-eslint", "ecmaFeatures": { "jsx": true }, "env": { "es6": true, "jasmine": true, }, "plugins": [ "react", "prettier", ], // Map from global var to bool specifying if it can be redefined "globals": { "__DEV__": true, "__dirname": false, "__fbBatchedBridgeConfig": false, "alert": false, "cancelAnimationFrame": false, "cancelIdleCallback": false, "clearImmediate": true, "clearInterval": false, "clearTimeout": false, "console": false, "document": false, "escape": false, "Event": false, "EventTarget": false, "exports": false, "fetch": false, "FormData": false, "global": false, "jest": false, "Map": true, "module": false, "navigator": false, "process": false, "Promise": true, "requestAnimationFrame": true, "requestIdleCallback": true, "require": false, "Set": true, "setImmediate": true, "setInterval": false, "setTimeout": false, "window": false, "XMLHttpRequest": false, "pit": false, // Flow global types. "ReactComponent": false, "ReactClass": false, "ReactElement": false, "ReactPropsCheckType": false, "ReactPropsChainableTypeChecker": false, "ReactPropTypes": false, "SyntheticEvent": false, "$Either": false, "$All": false, "$ArrayBufferView": false, "$Tuple": false, "$Supertype": false, "$Subtype": false, "$Shape": false, "$Diff": false, "$Keys": false, "$Enum": false, "$Exports": false, "$FlowIssue": false, "$FlowFixMe": false, "$FixMe": false }, "rules": { "comma-dangle": 0, // disallow trailing commas in object literals "no-cond-assign": 1, // disallow assignment in conditional expressions "no-console": 0, // disallow use of console (off by default in the node environment) "no-const-assign": 2, // disallow assignment to const-declared variables "no-constant-condition": 0, // disallow use of constant expressions in conditions "no-control-regex": 1, // disallow control characters in regular expressions "no-debugger": 1, // disallow use of debugger "no-dupe-keys": 1, // disallow duplicate keys when creating object literals "no-empty": 0, // disallow empty statements "no-ex-assign": 1, // disallow assigning to the exception in a catch block "no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) "no-extra-semi": 1, // disallow unnecessary semicolons "no-func-assign": 1, // disallow overwriting functions written as function declarations "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks "no-invalid-regexp": 1, // disallow invalid regular expression strings in the RegExp constructor "no-negated-in-lhs": 1, // disallow negation of the left operand of an in expression "no-obj-calls": 1, // disallow the use of object properties of the global object (Math and JSON) as functions "no-regex-spaces": 1, // disallow multiple spaces in a regular expression literal "no-reserved-keys": 0, // disallow reserved words being used as object literal keys (off by default) "no-sparse-arrays": 1, // disallow sparse arrays "no-unreachable": 1, // disallow unreachable statements after a return, throw, continue, or break statement "use-isnan": 1, // disallow comparisons with the value NaN "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default) "valid-typeof": 1, // Ensure that the results of typeof are compared against a valid string "no-var": 1, // discouraging the use of var and encouraging the use of const or let instead // Best Practices // These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns. "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default) "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) "consistent-return": 0, // require return statements to either always or never specify values "curly": 1, // specify curly brace conventions for all control statements "default-case": 0, // require default case in switch statements (off by default) "dot-notation": 1, // encourages use of dot notation whenever possible "eqeqeq": [1, "allow-null"], // require the use of === and !== "guard-for-in": 0, // make sure for-in loops have an if statement (off by default) "no-alert": 1, // disallow the use of alert, confirm, and prompt "no-caller": 1, // disallow use of arguments.caller or arguments.callee "no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression (off by default) "no-else-return": 0, // disallow else after a return in an if (off by default) "no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default) "no-eval": 1, // disallow use of eval() "no-extend-native": 1, // disallow adding to native types "no-extra-bind": 1, // disallow unnecessary function binding "no-fallthrough": 1, // disallow fallthrough of case statements "no-floating-decimal": 1, // disallow the use of leading or trailing decimal points in numeric literals (off by default) "no-implied-eval": 1, // disallow use of eval()-like methods "no-labels": 1, // disallow use of labeled statements "no-iterator": 1, // disallow usage of __iterator__ property "no-lone-blocks": 1, // disallow unnecessary nested blocks "no-loop-func": 0, // disallow creation of functions within loops "no-multi-str": 0, // disallow use of multiline strings "no-native-reassign": 0, // disallow reassignments of native objects "no-new": 1, // disallow use of new operator when not part of the assignment or comparison "no-new-func": 1, // disallow use of new operator for Function object "no-new-wrappers": 1, // disallows creating new instances of String,Number, and Boolean "no-octal": 1, // disallow use of octal literals "no-octal-escape": 1, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; "no-proto": 1, // disallow usage of __proto__ property "no-redeclare": 0, // disallow declaring the same variable more then once "no-return-assign": 1, // disallow use of assignment in return statement "no-script-url": 1, // disallow use of javascript: urls. "no-self-compare": 1, // disallow comparisons where both sides are exactly the same (off by default) "no-sequences": 1, // disallow use of comma operator "no-unused-expressions": 0, // disallow usage of expressions in statement position "no-void": 1, // disallow use of void operator (off by default) "no-warning-comments": 0, // disallow usage of configurable warning terms in comments": 1, // e.g. TODO or FIXME (off by default) "no-with": 1, // disallow use of the with statement "radix": 1, // require use of the second argument for parseInt() (off by default) "semi-spacing": 1, // require a space after a semi-colon "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default) "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default) "yoda": 1, // require or disallow Yoda conditions // Variables // These rules have to do with variable declarations. "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) "no-delete-var": 1, // disallow deletion of variables "no-label-var": 1, // disallow labels that share a name with a variable "no-shadow": 1, // disallow declaration of variables already declared in the outer scope "no-shadow-restricted-names": 1, // disallow shadowing of names such as arguments "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block "no-undefined": 0, // disallow use of undefined variable (off by default) "no-undef-init": 1, // disallow use of undefined when initializing variables "no-unused-vars": [1, {"vars": "all", "args": "none"}], // disallow declaration of variables that are not used in the code "no-use-before-define": 0, // disallow use of variables before they are defined // Node.js // These rules are specific to JavaScript running on Node.js. "handle-callback-err": 1, // enforces error handling in callbacks (off by default) (on by default in the node environment) "no-mixed-requires": 1, // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment) "no-new-require": 1, // disallow use of new operator with the require function (off by default) (on by default in the node environment) "no-path-concat": 1, // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment) "no-process-exit": 0, // disallow process.exit() (on by default in the node environment) "no-restricted-modules": 1, // restrict usage of specified node modules (off by default) "no-sync": 0, // disallow use of synchronous methods (off by default) // Stylistic Issues // These rules are purely matters of style and are quite subjective. "key-spacing": 0, "keyword-spacing": 1, // enforce spacing before and after keywords "jsx-quotes": [1, "prefer-double"], "comma-spacing": 0, "no-multi-spaces": 0, "brace-style": 0, // enforce one true brace style (off by default) "camelcase": 0, // require camel case names "consistent-this": [1, "self"], // enforces consistent naming when capturing the current execution context (off by default) "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines "func-names": 0, // require function expressions to have a name (off by default) "func-style": 0, // enforces use of function declarations or expressions (off by default) "new-cap": 0, // require a capital letter for constructors "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments "no-nested-ternary": 0, // disallow nested ternary expressions (off by default) "no-array-constructor": 1, // disallow use of the Array constructor "no-lonely-if": 0, // disallow if as the only statement in an else block (off by default) "no-new-object": 1, // disallow use of the Object constructor "no-spaced-func": 1, // disallow space between function identifier and application "no-ternary": 0, // disallow the use of ternary operators (off by default) "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines "no-underscore-dangle": 0, // disallow dangling underscores in identifiers "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation "quotes": [1, "double", "avoid-escape"], // specify whether double or single quotes should be used "quote-props": 0, // require quotes around object literal property names (off by default) "semi": 1, // require or disallow use of semicolons instead of ASI "sort-vars": 0, // sort variables within the same declaration block (off by default) "space-in-brackets": 0, // require or disallow spaces inside brackets (off by default) "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default) "space-infix-ops": 1, // require spaces around operators "space-unary-ops": [1, { "words": true, "nonwords": false }], // require or disallow spaces before/after unary operators (words on by default, nonwords off by default) "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default) "one-var": 0, // allow just one var statement per function (off by default) "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) // Legacy // 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. "max-depth": 0, // specify the maximum depth that blocks can be nested (off by default) "max-len": 0, // specify the maximum length of a line in your program (off by default) "max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default) "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) "no-bitwise": 1, // disallow use of bitwise operators (off by default) "no-plusplus": 0, // disallow use of unary operators, ++ and -- (off by default) // React Plugin // The following rules are made available via `eslint-plugin-react`. "react/display-name": 0, "react/jsx-boolean-value": 0, "react/jsx-no-duplicate-props": 2, "react/jsx-no-undef": 1, "react/jsx-sort-props": 0, "react/jsx-uses-react": 1, "react/jsx-uses-vars": 1, "react/no-did-mount-set-state": 1, "react/no-did-update-set-state": 1, "react/no-multi-comp": 0, "react/no-string-refs": 1, "react/no-unknown-property": 0, "react/prop-types": 0, "react/react-in-jsx-scope": 1, "react/self-closing-comp": 1, "react/wrap-multilines": 0, // Prettier Plugin // The following rules are made available via `eslint-plugin-prettier`. "prettier/prettier": "error", } } ================================================ FILE: .flowconfig ================================================ [ignore] ; We fork some components by platform .*/*[.]android.js ; Ignore "BUCK" generated dirs /\.buckd/ ; Ignore unexpected extra "@providesModule" .*/node_modules/.*/node_modules/fbjs/.* ; Ignore unrelated errors in node_modules .*/react-native/Libraries/.* .*/react-native/lib/.* .*/babel-plugin-relay/lib/.* .*/react-relay/lib/.* .*/relay-compiler/lib/.* .*/relay-runtime/lib/.* .*/graphql/.* .*/react-native-keyboard-aware-scroll-view/.* .*/react-native-linear-gradient/.* .*/react-native-deprecated-custom-components/src/flattenStyle.js [include] [libs] node_modules/react-native/Libraries/react-native/react-native-interface.js node_modules/react-native/flow/ flow/ js/flow-lib.js [options] emoji=true module.system=haste experimental.strict_type_args=true munge_underscores=true module.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' suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) suppress_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]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy unsafe.enable_getters_and_setters=true [version] ^0.42.0 ================================================ FILE: .gitattributes ================================================ *.pbxproj -text ================================================ FILE: .gitignore ================================================ # OSX # .DS_Store # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate project.xcworkspace # Android/IntelliJ # build/ .idea .gradle local.properties *.iml # node.js # node_modules/ npm-debug.log yarn-error.log package-lock.json # BUCK buck-out/ \.buckd/ *.keystore # fastlane # # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md fastlane/report.xml fastlane/Preview.html fastlane/screenshots ================================================ FILE: .watchmanconfig ================================================ {} ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Code of Conduct Facebook 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. ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to F8 App We want to make contributing to this project as easy and transparent as possible. ## Code of Conduct The code of conduct is described in [`CODE_OF_CONDUCT.md`](/CODE_OF_CONDUCT.md) ## Get involved There 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: * 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). * Look through the [open issues](https://github.com/fbsamples/f8app/issues). Provide workarounds, ask for clarification, or suggest labels. * 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. ## Pull Request Workflow _Before_ submitting a pull request, please make sure the following is done… 1. 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/ Open terminal (e.g. Terminal, iTerm, Git Bash or Git Shell) and type: ```sh git clone https://github.com//f8app cd f8app git checkout -b my_branch ``` Note: Replace `` with your GitHub username 2. Follow the [setup instructions](http://makeitopen.com/docs/en/1-A1-local-setup.html) to run the app and test your changes. 3. If you haven't already, complete the Contributor License Agreement ("CLA"). After 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. ## Contributor License Agreement ("CLA") In order to accept your pull request, we need you to submit a CLA. You only need to do this once to work on any of Facebook's open source projects. Complete your CLA here: ## Issues We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. ### Security Bugs Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue. ## Code Conventions * ES6 syntax when possible. * Use [Flow types](http://flowtype.org/). * Avd abbr wrds. ## License By contributing to this repo, you agree that your contributions will be licensed under its license. ================================================ FILE: LICENSE ================================================ Copyright 2016 Facebook, Inc. You are hereby granted a non-exclusive, worldwide, royalty-free license to use, copy, modify, and distribute this software in source code or binary form for use in connection with the web services and APIs provided by Facebook. As with any software that integrates with the Facebook platform, your use of this software is subject to the Facebook Developer Principles and Policies [http://developers.facebook.com/policy/]. This copyright notice shall be included in all copies or substantial portions of the software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ================================================ FILE: README.md ================================================ # F8 App 2017 This 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). ## How We Build It We'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. Check out how to set the app up for local development [here](http://makeitopen.com/docs/en/1-A1-local-setup.html)! ================================================ FILE: __mocks__/react-native.js ================================================ import rn from "react-native"; jest.mock("Linking", () => { return { addEventListener: jest.fn(), removeEventListener: jest.fn(), openURL: jest.fn(), canOpenURL: jest.fn(), getInitialURL: jest.fn() }; }); jest.mock("PushNotificationIOS", () => ({ addEventListener: jest.fn(), requestPermissions: jest.fn() })); module.exports = rn; ================================================ FILE: android/app/BUCK ================================================ import re # To learn about Buck see [Docs](https://buckbuild.com/). # To run your application with Buck: # - install Buck # - `npm start` - to start the packager # - `cd android` # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck # - `buck install -r android/app` - compile, install and run application # lib_deps = [] for jarfile in glob(['libs/*.jar']): name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) lib_deps.append(':' + name) prebuilt_jar( name = name, binary_jar = jarfile, ) for aarfile in glob(['libs/*.aar']): name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) lib_deps.append(':' + name) android_prebuilt_aar( name = name, aar = aarfile, ) android_library( name = 'all-libs', exported_deps = lib_deps ) android_library( name = 'app-code', srcs = glob([ 'src/main/java/**/*.java', ]), deps = [ ':all-libs', ':build_config', ':res', ], ) android_build_config( name = 'build_config', package = 'com.f82017', ) android_resource( name = 'res', res = 'src/main/res', package = 'com.f82017', ) android_binary( name = 'app', package_type = 'debug', manifest = 'src/main/AndroidManifest.xml', keystore = '//android/keystores:debug', deps = [ ':app-code', ], ) ================================================ FILE: android/app/build.gradle ================================================ apply plugin: "com.android.application" import com.android.build.OutputFile /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). * These basically call `react-native bundle` with the correct arguments during the Android build * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * * // the entry file for bundle generation * entryFile: "index.android.js", * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * * // whether to bundle JS and assets in release mode * bundleInRelease: true, * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants * // The configuration property can be in the following formats * // 'bundleIn${productFlavor}${buildType}' * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, * * // the root of your project, i.e. where "package.json" lives * root: "../../", * * // where to put the JS bundle asset in debug mode * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", * * // where to put the JS bundle asset in release mode * jsBundleDirRelease: "$buildDir/intermediates/assets/release", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in debug mode * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in release mode * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", * * // by default the gradle tasks are skipped if none of the JS files or assets change; this means * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to * // date; if you have any other folders that you want to ignore for performance reasons (gradle * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ * // for example, you might want to remove it from here. * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments * nodeExecutableAndArgs: ["node"] * * // supply additional arguments to the packager * extraPackagerArgs: [] * ] */ apply from: "../../node_modules/react-native/react.gradle" /** * Set this to true to create two separate APKs instead of one: * - An APK that only works on ARM devices * - An APK that only works on x86 devices * The advantage is the size of the APK is reduced by about 4MB. * Upload all the APKs to the Play Store and people will download * the correct one based on the CPU architecture of their device. */ def enableSeparateBuildPerCPUArchitecture = false /** * Run Proguard to shrink the Java bytecode in release builds. */ def enableProguardInReleaseBuilds = false android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.facebook.f8" minSdkVersion 16 targetSdkVersion 23 versionCode 10000410 versionName "4.0.0" ndk { abiFilters "armeabi-v7a", "x86" } } signingConfigs { release { storeFile file(F8_RELEASE_STORE_FILE) storePassword F8_RELEASE_STORE_PASSWORD keyAlias F8_RELEASE_KEY_ALIAS keyPassword F8_RELEASE_KEY_PASSWORD } } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include "armeabi-v7a", "x86" } } buildTypes { release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" signingConfig signingConfigs.release debuggable false } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits def versionCodes = ["armeabi-v7a":1, "x86":2] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode } } } } dependencies { compile project(':react-native-linear-gradient') compile project(':react-native-native-video-player') compile project(':react-native-push-notification') compile project(':react-native-photo-view') compile ('com.google.android.gms:play-services-gcm:10.0.1') // See https://stackoverflow.com/questions/44190829/facebook-sdk-android-error-building/44190896#44190896 compile(project(':react-native-fbsdk')){ exclude(group: 'com.facebook.android', module: 'facebook-android-sdk') } compile "com.facebook.android:facebook-android-sdk:4.22.1" compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.compile into 'libs' } ================================================ FILE: android/app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Disabling obfuscation is useful if you collect stack traces from production crashes # (unless you are using a system that supports de-obfuscate the stack traces). -dontobfuscate # React Native # Keep our interfaces so they can be used by other ProGuard rules. # See http://sourceforge.net/p/proguard/bugs/466/ -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip # Do not strip any method/class that is annotated with @DoNotStrip -keep @com.facebook.proguard.annotations.DoNotStrip class * -keep @com.facebook.common.internal.DoNotStrip class * -keepclassmembers class * { @com.facebook.proguard.annotations.DoNotStrip *; @com.facebook.common.internal.DoNotStrip *; } -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { void set*(***); *** get*(); } -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } -keep class * extends com.facebook.react.bridge.NativeModule { *; } -keepclassmembers,includedescriptorclasses class * { native ; } -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } -dontwarn com.facebook.react.** # okhttp -keepattributes Signature -keepattributes *Annotation* -keep class okhttp3.** { *; } -keep interface okhttp3.** { *; } -dontwarn okhttp3.** # okio -keep class sun.misc.Unsafe { *; } -dontwarn java.nio.file.* -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn okio.** ================================================ FILE: android/app/src/debug/res/values/strings.xml ================================================ F8 Debug 619048868222429 ================================================ FILE: android/app/src/debug/res/values/styles.xml ================================================ ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/java/com/facebook/f8/MainActivity.java ================================================ package com.facebook.f8; import com.facebook.react.ReactActivity; import android.content.Intent; /*import com.facebook.reactnative.androidsdk.FBSDKPackage; import com.microsoft.codepush.react.CodePush;*/ public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "F82017"; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data); } } ================================================ FILE: android/app/src/main/java/com/facebook/f8/MainApplication.java ================================================ package com.facebook.f8; import android.app.Application; import com.facebook.react.ReactApplication; import com.BV.LinearGradient.LinearGradientPackage; import com.wog.videoplayer.VideoPlayerPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.reactnative.photoview.PhotoViewPackage; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import com.facebook.CallbackManager; import com.facebook.FacebookSdk; import com.facebook.reactnative.androidsdk.FBSDKPackage; import com.facebook.appevents.AppEventsLogger; import java.util.Arrays; import java.util.List; public class MainApplication extends Application implements ReactApplication { private static CallbackManager mCallbackManager = CallbackManager.Factory.create(); protected static CallbackManager getCallbackManager() { return mCallbackManager; } private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List getPackages() { return Arrays.asList( new MainReactPackage(), new VideoPlayerPackage(), new PhotoViewPackage(), new FBSDKPackage(mCallbackManager), new ReactNativePushNotificationPackage(), new LinearGradientPackage() ); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); FacebookSdk.sdkInitialize(getApplicationContext()); AppEventsLogger.activateApp(this); } } ================================================ FILE: android/app/src/main/res/values/strings.xml ================================================ F8 619048868222429 ================================================ FILE: android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android/build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { mavenLocal() jcenter() google() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url "$rootDir/../node_modules/react-native/android" } } } ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ #Tue Jan 17 15:39:05 EST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-all.zip ================================================ FILE: android/gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useDeprecatedNdk=true F8_RELEASE_STORE_FILE=f8-release.keystore F8_RELEASE_KEY_ALIAS=f8-release F8_RELEASE_STORE_PASSWORD=K0IvEbWKWyrZR3KpbfDi F8_RELEASE_KEY_PASSWORD=D7UhkezSHJqXbBVlAlxA ================================================ FILE: android/gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: android/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: android/keystores/BUCK ================================================ keystore( name = 'debug', store = 'debug.keystore', properties = 'debug.keystore.properties', visibility = [ 'PUBLIC', ], ) ================================================ FILE: android/keystores/debug.keystore.properties ================================================ key.store=debug.keystore key.alias=androiddebugkey key.store.password=android key.alias.password=android ================================================ FILE: android/settings.gradle ================================================ rootProject.name = 'F82017' include ':react-native-linear-gradient' project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') include ':react-native-native-video-player' project(':react-native-native-video-player').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-native-video-player/android') include ':react-native-push-notification' project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android') include ':react-native-photo-view' project(':react-native-photo-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-photo-view/android') include ':app' include ':react-native-fbsdk' project(':react-native-fbsdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fbsdk/android') ================================================ FILE: app.json ================================================ { "name": "F82017", "displayName": "F82017" } ================================================ FILE: docker-compose.yml ================================================ version: "3" services: mongo: image: mongo networks: default: aliases: - mongo mongorestore: build: ./server/mongorestore depends_on: - mongo parse: build: ./server/parse-server ports: - "1337:1337" depends_on: - mongo parse_dashboard: build: ./server/parse-dashboard ports: - "4040:4040" graphql: build: ./server/graphql environment: - PARSE_URL=http://parse:1337/parse ports: - "4000:4000" ================================================ FILE: index.android.js ================================================ import { AppRegistry } from "react-native"; import setup from "./js/setup"; AppRegistry.registerComponent("F82017", setup); ================================================ FILE: index.ios.js ================================================ import { AppRegistry } from "react-native"; import setup from "./js/setup"; AppRegistry.registerComponent("F82017", setup); ================================================ FILE: ios/F82017/AppDelegate.h ================================================ /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import @interface AppDelegate : UIResponder @property (nonatomic, strong) UIWindow *window; @end ================================================ FILE: ios/F82017/AppDelegate.m ================================================ /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import "AppDelegate.h" #import #import #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; #ifdef DEBUG jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; #else jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"F82017" initialProperties:nil launchOptions:launchOptions]; rootView.backgroundColor = [[UIColor alloc] initWithRed:0.98f green:0.98f blue:0.94f alpha:1]; NSArray *objects = [[NSBundle mainBundle] loadNibNamed:@"LaunchScreen" owner:self options:nil]; UIImageView *loadingView = [[[objects objectAtIndex:0] subviews] objectAtIndex:0]; loadingView = [[UIImageView alloc] initWithImage:[loadingView image]]; loadingView.frame = [UIScreen mainScreen].bounds; loadingView.contentMode = UIViewContentModeScaleAspectFill; rootView.loadingView = loadingView; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey] ]; // Add any custom logic here. return handled; } - (void)applicationDidBecomeActive:(UIApplication *)application { [FBSDKAppEvents activateApp]; } // Required to register for notifications - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { [RCTPushNotificationManager didRegisterUserNotificationSettings:notificationSettings]; } // Required for the register event. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } // Required for the notification event. You must call the completion handler after handling the remote notification. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { { [RCTPushNotificationManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } } // Optionally implement this method over the previous to receive remote notifications. However // implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible. // If your delegate implements both methods, the app object calls the `application:didReceiveRemoteNotification:fetchCompletionHandler:` method // Either this method or `application:didReceiveRemoteNotification:fetchCompletionHandler:` is required in order to receive remote notifications. // // Required for the registrationError event. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error]; } // Required for the notification event. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification { [RCTPushNotificationManager didReceiveRemoteNotification:notification]; } // Required for the localNotification event. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [RCTPushNotificationManager didReceiveLocalNotification:notification]; } @end ================================================ FILE: ios/F82017/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: ios/F82017/F82017.entitlements ================================================ aps-environment development ================================================ FILE: ios/F82017/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "20x20", "idiom" : "iphone", "filename" : "20@2x.png", "scale" : "2x" }, { "size" : "20x20", "idiom" : "iphone", "filename" : "20@3x.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "29@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "29@3x.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "40@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "40@3x.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "60@2x.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "60@3x.png", "scale" : "3x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/F82017/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName F8 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 4.0.0 CFBundleSignature ???? CFBundleURLTypes CFBundleURLSchemes fb619048868222429 f8 CFBundleVersion 410 FacebookAppID 619048868222429 FacebookDisplayName F8 Developer Conference 2017 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes fbapi fb-messenger-api fbauth2 fbshareextension LSRequiresIPhoneOS NSAppTransportSecurity NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads NSAllowsArbitraryLoads NSLocationWhenInUseUsageDescription NSPhotoLibraryUsageDescription Required for use with the FacebookSDK UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIViewControllerBasedStatusBarAppearance ================================================ FILE: ios/F82017/main.m ================================================ /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: ios/F82017-tvOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance NSLocationWhenInUseUsageDescription NSAppTransportSecurity NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads ================================================ FILE: ios/F82017-tvOSTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: ios/F82017.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00E356F31AD99517003FC87E /* F82017Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* F82017Tests.m */; }; 0186555E1614488AAE4A6B43 /* libRNVideoPlayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; }; 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; }; 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; }; 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; }; 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; }; 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; 2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; 2DCD954D1E0B4F2C00145EB5 /* F82017Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* F82017Tests.m */; }; 3009A9271DF88122006B5D62 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9231DF88122006B5D62 /* Bolts.framework */; }; 3009A9281DF88122006B5D62 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */; }; 3009A9291DF88122006B5D62 /* FBSDKLoginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */; }; 3009A92A1DF88122006B5D62 /* FBSDKShareKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */; }; 3035251A1E4BFD4E0003A60D /* launchscreen.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525171E4BFD4E0003A60D /* launchscreen.png */; }; 3035251B1E4BFD4E0003A60D /* launchscreen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525181E4BFD4E0003A60D /* launchscreen@2x.png */; }; 3035251C1E4BFD4E0003A60D /* launchscreen@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 303525191E4BFD4E0003A60D /* launchscreen@3x.png */; }; 308E35A01E8C2A5E00CAC09E /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */; }; 309371D11E2DBE160019E3C2 /* 512.png in Resources */ = {isa = PBXBuildFile; fileRef = 309371D01E2DBE160019E3C2 /* 512.png */; }; 309371D31E2DBEAE0019E3C2 /* 512@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 309371D21E2DBEAE0019E3C2 /* 512@2x.png */; }; 3098A8FF1E7C64CD0003742F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3098A8FE1E7C64CD0003742F /* libz.tbd */; }; 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; B2C3247ED20945EDAAD12F70 /* libRCTFBSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E12A673783B9486B87E8AE7B /* libRCTFBSDK.a */; }; FC554BF677604211A694CB54 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTActionSheet; }; 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTGeolocation; }; 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B5115D1A9E6B3D00147676; remoteInfo = RCTImage; }; 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B511DB1A9E6C8500147676; remoteInfo = RCTNetwork; }; 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; proxyType = 2; remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; remoteInfo = RCTVibration; }; 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; proxyType = 1; remoteGlobalIDString = 13B07F861A680F5B00A75B9A; remoteInfo = F82017; }; 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTSettings; }; 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3C86DF461ADF2C930047B81A; remoteInfo = RCTWebSocket; }; 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteInfo = React; }; 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; proxyType = 1; remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; remoteInfo = "F82017-tvOS"; }; 308E335D1E8C1E0800CAC09E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = AC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */; proxyType = 2; remoteGlobalIDString = 9350E0F11CE3B0920041D815; remoteInfo = RCTFBSDK; }; 308E35411E8C280700CAC09E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */; proxyType = 2; remoteGlobalIDString = 84BF382F1DBF52980044F03A; remoteInfo = RNVideoPlayer; }; 308E359C1E8C2A3D00CAC09E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTPushNotification; }; 308E359E1E8C2A3D00CAC09E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D05745F1DE6004600184BB4; remoteInfo = "RCTPushNotification-tvOS"; }; 308E376E1E8D63B000CAC09E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = BVLinearGradient; }; 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; remoteInfo = "RCTImage-tvOS"; }; 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28471D9B043800D4039D; remoteInfo = "RCTLinking-tvOS"; }; 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28541D9B044C00D4039D; remoteInfo = "RCTNetwork-tvOS"; }; 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28611D9B046600D4039D; remoteInfo = "RCTSettings-tvOS"; }; 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A287B1D9B048500D4039D; remoteInfo = "RCTText-tvOS"; }; 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28881D9B049200D4039D; remoteInfo = "RCTWebSocket-tvOS"; }; 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28131D9B038B00D4039D; remoteInfo = "React-tvOS"; }; 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C059A1DE3340900C268FA; remoteInfo = yoga; }; 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C06751DE3340C00C268FA; remoteInfo = "yoga-tvOS"; }; 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; remoteInfo = cxxreact; }; 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; remoteInfo = "cxxreact-tvOS"; }; 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; remoteInfo = jschelpers; }; 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteInfo = "jschelpers-tvOS"; }; 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTAnimation; }; 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28201D9B03D100D4039D; remoteInfo = "RCTAnimation-tvOS"; }; 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; proxyType = 2; remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTLinking; }; 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 00E356EE1AD99517003FC87E /* F82017Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = F82017Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* F82017Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = F82017Tests.m; sourceTree = ""; }; 0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVideoPlayer.a; sourceTree = ""; }; 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* F82017.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = F82017.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = F82017/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = F82017/AppDelegate.m; sourceTree = ""; }; 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = F82017/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = F82017/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = F82017/main.m; sourceTree = ""; }; 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "F82017-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "F82017-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 3009A9231DF88122006B5D62 /* Bolts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bolts.framework; path = ../../../../../../Documents/FacebookSDK/Bolts.framework; sourceTree = ""; }; 3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKCoreKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKCoreKit.framework; sourceTree = ""; }; 3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKLoginKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKLoginKit.framework; sourceTree = ""; }; 3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FBSDKShareKit.framework; path = ../../../../../../Documents/FacebookSDK/FBSDKShareKit.framework; sourceTree = ""; }; 303525171E4BFD4E0003A60D /* launchscreen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = launchscreen.png; sourceTree = ""; }; 303525181E4BFD4E0003A60D /* launchscreen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "launchscreen@2x.png"; sourceTree = ""; }; 303525191E4BFD4E0003A60D /* launchscreen@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "launchscreen@3x.png"; sourceTree = ""; }; 30391C631E48DF35005B8B86 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 30391C651E48DF3F005B8B86 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 30391C671E48DF4D005B8B86 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; 307A4B9C1E1D991A0062A662 /* F82017.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = F82017.entitlements; path = F82017/F82017.entitlements; sourceTree = ""; }; 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = ""; }; 309371D01E2DBE160019E3C2 /* 512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 512.png; sourceTree = ""; }; 309371D21E2DBEAE0019E3C2 /* 512@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "512@2x.png"; sourceTree = ""; }; 3098A8FE1E7C64CD0003742F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 75B177E8AB114A5C9AE0BF0C /* 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 = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; AC84A694044143A5965F8E75 /* 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 = ""; }; D88E526290554232ABA3F7BC /* 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 = ""; }; E12A673783B9486B87E8AE7B /* libRCTFBSDK.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTFBSDK.a; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 00E356EB1AD99517003FC87E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 308E35A01E8C2A5E00CAC09E /* libRCTPushNotification.a in Frameworks */, 3009A9291DF88122006B5D62 /* FBSDKLoginKit.framework in Frameworks */, 3009A9271DF88122006B5D62 /* Bolts.framework in Frameworks */, 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 3009A92A1DF88122006B5D62 /* FBSDKShareKit.framework in Frameworks */, 3009A9281DF88122006B5D62 /* FBSDKCoreKit.framework in Frameworks */, B2C3247ED20945EDAAD12F70 /* libRCTFBSDK.a in Frameworks */, FC554BF677604211A694CB54 /* libBVLinearGradient.a in Frameworks */, 0186555E1614488AAE4A6B43 /* libRNVideoPlayer.a in Frameworks */, 3098A8FF1E7C64CD0003742F /* libz.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */, 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */, 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */, 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */, 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */, 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */, 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */, 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 00C302A81ABCB8CE00DB3ED1 /* Products */ = { isa = PBXGroup; children = ( 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, ); name = Products; sourceTree = ""; }; 00C302B61ABCB90400DB3ED1 /* Products */ = { isa = PBXGroup; children = ( 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, ); name = Products; sourceTree = ""; }; 00C302BC1ABCB91800DB3ED1 /* Products */ = { isa = PBXGroup; children = ( 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, ); name = Products; sourceTree = ""; }; 00C302D41ABCB9D200DB3ED1 /* Products */ = { isa = PBXGroup; children = ( 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, ); name = Products; sourceTree = ""; }; 00C302E01ABCB9EE00DB3ED1 /* Products */ = { isa = PBXGroup; children = ( 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, ); name = Products; sourceTree = ""; }; 00E356EF1AD99517003FC87E /* F82017Tests */ = { isa = PBXGroup; children = ( 00E356F21AD99517003FC87E /* F82017Tests.m */, 00E356F01AD99517003FC87E /* Supporting Files */, ); path = F82017Tests; sourceTree = ""; }; 00E356F01AD99517003FC87E /* Supporting Files */ = { isa = PBXGroup; children = ( 00E356F11AD99517003FC87E /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 139105B71AF99BAD00B5F7CC /* Products */ = { isa = PBXGroup; children = ( 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, ); name = Products; sourceTree = ""; }; 139FDEE71B06529A00C62182 /* Products */ = { isa = PBXGroup; children = ( 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, ); name = Products; sourceTree = ""; }; 13B07FAE1A68108700A75B9A /* F82017 */ = { isa = PBXGroup; children = ( 307A4B9C1E1D991A0062A662 /* F82017.entitlements */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.m */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, ); name = F82017; sourceTree = ""; }; 146834001AC3E56700842450 /* Products */ = { isa = PBXGroup; children = ( 146834041AC3E56700842450 /* libReact.a */, 3DAD3EA31DF850E9000B6D8A /* libReact.a */, 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, ); name = Products; sourceTree = ""; }; 3006989C1F0338030070FAAA /* Recovered References */ = { isa = PBXGroup; children = ( E12A673783B9486B87E8AE7B /* libRCTFBSDK.a */, 8428BBB5DFD14C4AA4194341 /* libBVLinearGradient.a */, 0547D6774A7C4B49AF5E87E4 /* libRNVideoPlayer.a */, 309371D21E2DBEAE0019E3C2 /* 512@2x.png */, 303525171E4BFD4E0003A60D /* launchscreen.png */, 303525181E4BFD4E0003A60D /* launchscreen@2x.png */, 309371D01E2DBE160019E3C2 /* 512.png */, 303525191E4BFD4E0003A60D /* launchscreen@3x.png */, ); name = "Recovered References"; sourceTree = ""; }; 308E33481E8C1E0800CAC09E /* Products */ = { isa = PBXGroup; children = ( 308E335E1E8C1E0800CAC09E /* libRCTFBSDK.a */, ); name = Products; sourceTree = ""; }; 308E353E1E8C280700CAC09E /* Products */ = { isa = PBXGroup; children = ( 308E35421E8C280700CAC09E /* libRNVideoPlayer.a */, ); name = Products; sourceTree = ""; }; 308E35981E8C2A3D00CAC09E /* Products */ = { isa = PBXGroup; children = ( 308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */, 308E359F1E8C2A3D00CAC09E /* libRCTPushNotification-tvOS.a */, ); name = Products; sourceTree = ""; }; 308E376B1E8D63B000CAC09E /* Products */ = { isa = PBXGroup; children = ( 308E376F1E8D63B000CAC09E /* libBVLinearGradient.a */, ); name = Products; sourceTree = ""; }; 5E91572E1DD0AC6500FF2AA8 /* Products */ = { isa = PBXGroup; children = ( 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, ); name = Products; sourceTree = ""; }; 78C398B11ACF4ADC00677621 /* Products */ = { isa = PBXGroup; children = ( 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, ); name = Products; sourceTree = ""; }; 7A3404E71E26487D967C39EB /* Frameworks */ = { isa = PBXGroup; children = ( 3098A8FE1E7C64CD0003742F /* libz.tbd */, 30391C671E48DF4D005B8B86 /* libsqlite3.0.tbd */, 30391C651E48DF3F005B8B86 /* SystemConfiguration.framework */, 30391C631E48DF35005B8B86 /* CoreData.framework */, 3009A9231DF88122006B5D62 /* Bolts.framework */, 3009A9241DF88122006B5D62 /* FBSDKCoreKit.framework */, 3009A9251DF88122006B5D62 /* FBSDKLoginKit.framework */, 3009A9261DF88122006B5D62 /* FBSDKShareKit.framework */, ); name = Frameworks; sourceTree = ""; }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */, 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, AC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */, D88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */, 75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */, ); name = Libraries; sourceTree = ""; }; 832341B11AAA6A8300B99B32 /* Products */ = { isa = PBXGroup; children = ( 832341B51AAA6A8300B99B32 /* libRCTText.a */, 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, ); name = Products; sourceTree = ""; }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( 13B07FAE1A68108700A75B9A /* F82017 */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 00E356EF1AD99517003FC87E /* F82017Tests */, 83CBBA001A601CBA00E9B192 /* Products */, 7A3404E71E26487D967C39EB /* Frameworks */, B6298A78CBB448B794708EE9 /* Resources */, 3006989C1F0338030070FAAA /* Recovered References */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; }; 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* F82017.app */, 00E356EE1AD99517003FC87E /* F82017Tests.xctest */, 2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */, 2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */, ); name = Products; sourceTree = ""; }; B6298A78CBB448B794708EE9 /* Resources */ = { isa = PBXGroup; children = ( ); name = Resources; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 00E356ED1AD99517003FC87E /* F82017Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "F82017Tests" */; buildPhases = ( 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, ); buildRules = ( ); dependencies = ( 00E356F51AD99517003FC87E /* PBXTargetDependency */, ); name = F82017Tests; productName = F82017Tests; productReference = 00E356EE1AD99517003FC87E /* F82017Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 13B07F861A680F5B00A75B9A /* F82017 */ = { isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "F82017" */; buildPhases = ( 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, ); buildRules = ( ); dependencies = ( ); name = F82017; productName = "Hello World"; productReference = 13B07F961A680F5B00A75B9A /* F82017.app */; productType = "com.apple.product-type.application"; }; 2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "F82017-tvOS" */; buildPhases = ( 2D02E4771E0B4A5D006451C7 /* Sources */, 2D02E4781E0B4A5D006451C7 /* Frameworks */, 2D02E4791E0B4A5D006451C7 /* Resources */, 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, ); buildRules = ( ); dependencies = ( ); name = "F82017-tvOS"; productName = "F82017-tvOS"; productReference = 2D02E47B1E0B4A5D006451C7 /* F82017-tvOS.app */; productType = "com.apple.product-type.application"; }; 2D02E48F1E0B4A5D006451C7 /* F82017-tvOSTests */ = { isa = PBXNativeTarget; buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "F82017-tvOSTests" */; buildPhases = ( 2D02E48C1E0B4A5D006451C7 /* Sources */, 2D02E48D1E0B4A5D006451C7 /* Frameworks */, 2D02E48E1E0B4A5D006451C7 /* Resources */, ); buildRules = ( ); dependencies = ( 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, ); name = "F82017-tvOSTests"; productName = "F82017-tvOSTests"; productReference = 2D02E4901E0B4A5D006451C7 /* F82017-tvOSTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 610; ORGANIZATIONNAME = Facebook; TargetAttributes = { 00E356ED1AD99517003FC87E = { CreatedOnToolsVersion = 6.2; TestTargetID = 13B07F861A680F5B00A75B9A; }; 13B07F861A680F5B00A75B9A = { DevelopmentTeam = 9B8M3M76P9; SystemCapabilities = { com.apple.Push = { enabled = 1; }; }; }; 2D02E47A1E0B4A5D006451C7 = { CreatedOnToolsVersion = 8.2.1; ProvisioningStyle = Automatic; }; 2D02E48F1E0B4A5D006451C7 = { CreatedOnToolsVersion = 8.2.1; ProvisioningStyle = Automatic; TestTargetID = 2D02E47A1E0B4A5D006451C7; }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "F82017" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 308E376B1E8D63B000CAC09E /* Products */; ProjectRef = 75B177E8AB114A5C9AE0BF0C /* BVLinearGradient.xcodeproj */; }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; }, { ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; }, { ProductGroup = 308E33481E8C1E0800CAC09E /* Products */; ProjectRef = AC84A694044143A5965F8E75 /* RCTFBSDK.xcodeproj */; }, { ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; }, { ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; }, { ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; }, { ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; }, { ProductGroup = 308E35981E8C2A3D00CAC09E /* Products */; ProjectRef = 308E35971E8C2A3D00CAC09E /* RCTPushNotification.xcodeproj */; }, { ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; }, { ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; }, { ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; }, { ProductGroup = 139FDEE71B06529A00C62182 /* Products */; ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; }, { ProductGroup = 146834001AC3E56700842450 /* Products */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; }, { ProductGroup = 308E353E1E8C280700CAC09E /* Products */; ProjectRef = D88E526290554232ABA3F7BC /* RNVideoPlayer.xcodeproj */; }, ); projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* F82017 */, 00E356ED1AD99517003FC87E /* F82017Tests */, 2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */, 2D02E48F1E0B4A5D006451C7 /* F82017-tvOSTests */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTActionSheet.a; remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTGeolocation.a; remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTImage.a; remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTNetwork.a; remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTVibration.a; remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTSettings.a; remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTWebSocket.a; remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 146834041AC3E56700842450 /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 308E335E1E8C1E0800CAC09E /* libRCTFBSDK.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTFBSDK.a; remoteRef = 308E335D1E8C1E0800CAC09E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 308E35421E8C280700CAC09E /* libRNVideoPlayer.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRNVideoPlayer.a; remoteRef = 308E35411E8C280700CAC09E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 308E359D1E8C2A3D00CAC09E /* libRCTPushNotification.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTPushNotification.a; remoteRef = 308E359C1E8C2A3D00CAC09E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 308E359F1E8C2A3D00CAC09E /* libRCTPushNotification-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTPushNotification-tvOS.a"; remoteRef = 308E359E1E8C2A3D00CAC09E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 308E376F1E8D63B000CAC09E /* libBVLinearGradient.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libBVLinearGradient.a; remoteRef = 308E376E1E8D63B000CAC09E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTImage-tvOS.a"; remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTLinking-tvOS.a"; remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTNetwork-tvOS.a"; remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTSettings-tvOS.a"; remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTText-tvOS.a"; remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libRCTWebSocket-tvOS.a"; remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTAnimation.a; remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTAnimation.a; remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTLinking.a; remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTText.a; remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 00E356EC1AD99517003FC87E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 309371D31E2DBEAE0019E3C2 /* 512@2x.png in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 3035251A1E4BFD4E0003A60D /* launchscreen.png in Resources */, 3035251B1E4BFD4E0003A60D /* launchscreen@2x.png in Resources */, 309371D11E2DBE160019E3C2 /* 512.png in Resources */, 3035251C1E4BFD4E0003A60D /* launchscreen@3x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E4791E0B4A5D006451C7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E48E1E0B4A5D006451C7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Bundle React Native code and images"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; }; 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Bundle React Native Code And Images"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 00E356EA1AD99517003FC87E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 00E356F31AD99517003FC87E /* F82017Tests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 13B07F871A680F5B00A75B9A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E4771E0B4A5D006451C7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 2D02E48C1E0B4A5D006451C7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 2DCD954D1E0B4F2C00145EB5 /* F82017Tests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 13B07F861A680F5B00A75B9A /* F82017 */; targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; }; 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 2D02E47A1E0B4A5D006451C7 /* F82017-tvOS */; targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 13B07FB21A68108700A75B9A /* Base */, ); name = LaunchScreen.xib; path = F82017; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", ); INFOPLIST_FILE = F82017Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "-ObjC", "-lc++", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/F82017.app/F82017"; }; name = Debug; }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", ); INFOPLIST_FILE = F82017Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "-ObjC", "-lc++", ); PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/F82017.app/F82017"; }; name = Release; }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODEPUSH_KEY = "o5k128-QbdToCO12IQl4zgJlxZQbVkEG2YjY-"; CODE_SIGN_ENTITLEMENTS = F82017/F82017.entitlements; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = 9B8M3M76P9; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native/React/**", ); INFOPLIST_FILE = F82017/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = com.parse.f8; PRODUCT_NAME = F82017; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODEPUSH_KEY = "qFZ_DANxpdRqMzw6anqVg9Z0D_RuVkEG2YjY-"; CODE_SIGN_ENTITLEMENTS = F82017/F82017.entitlements; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 9B8M3M76P9; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native/React/**", ); INFOPLIST_FILE = F82017/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = com.parse.f8; PRODUCT_NAME = F82017; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; 2D02E4971E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", ); INFOPLIST_FILE = "F82017-tvOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.F82017-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Debug; }; 2D02E4981E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_NO_COMMON_BLOCKS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", ); INFOPLIST_FILE = "F82017-tvOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.F82017-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Release; }; 2D02E4991E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "F82017-tvOSTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.F82017-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/F82017-tvOS.app/F82017-tvOS"; TVOS_DEPLOYMENT_TARGET = 10.1; }; name = Debug; }; 2D02E49A1E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = "F82017-tvOSTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.F82017-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/F82017-tvOS.app/F82017-tvOS"; TVOS_DEPLOYMENT_TARGET = 10.1; }; name = Release; }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = "~/Documents/FacebookSDK"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ""; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 83CBBA211A601CBA00E9B192 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = "~/Documents/FacebookSDK"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ""; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "F82017Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 00E356F61AD99517003FC87E /* Debug */, 00E356F71AD99517003FC87E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "F82017" */ = { isa = XCConfigurationList; buildConfigurations = ( 13B07F941A680F5B00A75B9A /* Debug */, 13B07F951A680F5B00A75B9A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "F82017-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 2D02E4971E0B4A5E006451C7 /* Debug */, 2D02E4981E0B4A5E006451C7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "F82017-tvOSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 2D02E4991E0B4A5E006451C7 /* Debug */, 2D02E49A1E0B4A5E006451C7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "F82017" */ = { isa = XCConfigurationList; buildConfigurations = ( 83CBBA201A601CBA00E9B192 /* Debug */, 83CBBA211A601CBA00E9B192 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; } ================================================ FILE: ios/F82017.xcodeproj/xcshareddata/xcschemes/F82017-tvOS.xcscheme ================================================ ================================================ FILE: ios/F82017.xcodeproj/xcshareddata/xcschemes/F82017.xcscheme ================================================ ================================================ FILE: ios/F82017Tests/F82017Tests.m ================================================ /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import #import #import #import #define TIMEOUT_SECONDS 600 #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" @interface F82017Tests : XCTestCase @end @implementation F82017Tests - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test { if (test(view)) { return YES; } for (UIView *subview in [view subviews]) { if ([self findSubviewInView:subview matching:test]) { return YES; } } return NO; } - (void)testRendersWelcomeScreen { UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; BOOL foundElement = NO; __block NSString *redboxError = nil; RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { if (level >= RCTLogLevelError) { redboxError = message; } }); while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { return YES; } return NO; }]; } RCTSetLogFunction(RCTDefaultLogFunction); XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); } @end ================================================ FILE: ios/F82017Tests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: js/F8Analytics.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ import { AppEventsLogger } from "react-native-fbsdk"; export default class F8Analytics { static logEvent(name, value, opts) { AppEventsLogger.logEvent(name, value, opts); } } ================================================ FILE: js/F8App.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { AppState, StyleSheet, StatusBar, View } from "react-native"; import LoginScreen from "./login/LoginScreen"; import PushNotificationsController from "./PushNotificationsController"; import F8Navigator from "./F8Navigator"; import { loadConfig, loadMaps, loadNotifications, loadSessions, loadFAQs, loadPages, loadFriendsSchedules, loadSurveys, loadVideos, loadPolicies, restoreSchedule } from "./actions"; import { updateInstallation } from "./actions/installation"; import { connect } from "react-redux"; import { version } from "./env.js"; class F8App extends React.Component { componentDidMount() { AppState.addEventListener("change", this.handleAppStateChange); // TODO: Make this list smaller, we basically download the whole internet this.props.dispatch(loadSessions()); this.props.dispatch(loadConfig()); this.props.dispatch(loadNotifications()); this.props.dispatch(loadVideos()); this.props.dispatch(loadMaps()); this.props.dispatch(loadFAQs()); this.props.dispatch(loadPages()); this.props.dispatch(loadPolicies()); if (this.props.isLoggedIn) { this.props.dispatch(restoreSchedule()); this.props.dispatch(loadSurveys()); this.props.dispatch(loadFriendsSchedules()); } updateInstallation({ version }); } componentWillUnmount() { AppState.removeEventListener("change", this.handleAppStateChange); } handleAppStateChange = appState => { if (appState === "active") { this.props.dispatch(loadSessions()); this.props.dispatch(loadVideos()); this.props.dispatch(loadNotifications()); if (this.props.isLoggedIn) { this.props.dispatch(restoreSchedule()); this.props.dispatch(loadSurveys()); } } }; render() { if (!this.props.skipWelcomeScreen) { return ; } return ( ); } } const styles = StyleSheet.create({ container: { flex: 1 } }); function select(store) { return { isLoggedIn: store.user.isLoggedIn, skipWelcomeScreen: store.user.isLoggedIn || store.user.hasSkippedLogin }; } module.exports = connect(select)(F8App); ================================================ FILE: js/F8Navigator.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import Platform from "Platform"; import BackAndroid from "BackAndroid"; import F8TabsView from "./tabs/F8TabsView"; import FriendsScheduleView from "./tabs/schedule/FriendsScheduleView"; import FilterScreen from "./filter/FilterScreen"; import LoginModal from "./login/LoginModal"; import { Navigator } from "react-native-deprecated-custom-components"; import SessionsCarousel from "./tabs/schedule/SessionsCarousel"; import SharingSettingsScreen from "./tabs/schedule/SharingSettingsScreen"; import F8WebView from "./common/F8WebView"; import RatingScreen from "./rating/RatingScreen"; import { StyleSheet } from "react-native"; import { connect } from "react-redux"; import F8Colors from "./common/F8Colors"; import F8VideoView from "./tabs/videos/F8VideoView"; import { switchTab } from "./actions"; import F8MapView from "./tabs/maps/F8MapView"; import DemosCarousel from "./tabs/demos/DemosCarousel"; const F8Navigator = React.createClass({ _handlers: ([]: Array<() => boolean>), componentDidMount: function() { BackAndroid.addEventListener("hardwareBackPress", this.handleBackButton); }, componentWillUnmount: function() { BackAndroid.removeEventListener("hardwareBackPress", this.handleBackButton); }, getChildContext() { return { addBackButtonListener: this.addBackButtonListener, removeBackButtonListener: this.removeBackButtonListener }; }, addBackButtonListener: function(listener) { this._handlers.push(listener); }, removeBackButtonListener: function(listener) { this._handlers = this._handlers.filter(handler => handler !== listener); }, handleBackButton: function() { for (let i = this._handlers.length - 1; i >= 0; i--) { if (this._handlers[i]()) { return true; } } const navigator = this._navigator; if (navigator && navigator.getCurrentRoutes().length > 1) { navigator.pop(); return true; } if (this.props.tab !== "schedule") { this.props.dispatch(switchTab("schedule")); return true; } return false; }, render: function() { return ( (this._navigator = c)} style={styles.container} configureScene={route => { if (Platform.OS === "android") { return Navigator.SceneConfigs.FloatFromBottomAndroid; } // TODO: Proper scene support if ( route.shareSettings || route.friend || route.webview || route.video || route.session || route.allSession || route.allDemos ) { return Navigator.SceneConfigs.PushFromRight; } else { return Navigator.SceneConfigs.FloatFromBottom; } }} initialRoute={{}} renderScene={this.renderScene} /> ); }, renderScene: function(route, navigator) { if (route.allSessions) { return ; } else if (route.session) { return ; } else if (route.filter) { return ; } else if (route.friend) { return ( ); } else if (route.login) { return ; } else if (route.shareSettings) { // else if (route.share){ return ; } return ; } else if (route.rate) { return ; } else if (route.webview) { return ; } else if (route.video) { return ; } else if (route.maps) { return ; } else if (route.allDemos) { return ; } else { return ; } } }); F8Navigator.childContextTypes = { addBackButtonListener: React.PropTypes.func, removeBackButtonListener: React.PropTypes.func }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.bianca } }); function select(store) { return { tab: store.navigation.tab, isLoggedIn: store.user.isLoggedIn || store.user.hasSkippedLogin }; } module.exports = connect(select)(F8Navigator); ================================================ FILE: js/FacebookSDK.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * This module implements a part of Facebook JavaScript SDK * https://developers.facebook.com/docs/javascript/reference/v2.2 * * @flow */ "use strict"; import { LoginManager, AccessToken, GraphRequest, GraphRequestManager } from "react-native-fbsdk"; const emptyFunction = () => {}; import mapObject from "fbjs/lib/mapObject"; type AuthResponse = { userID: string, accessToken: string, expiresIn: number }; type LoginOptions = { scope: string }; type LoginCallback = (result: { authResponse?: AuthResponse, error?: Error }) => void; let _authResponse: ?AuthResponse = null; async function loginWithFacebookSDK( options: LoginOptions ): Promise { const scope = options.scope || "public_profile"; const permissions = scope.split(","); const loginResult = await LoginManager.logInWithReadPermissions(permissions); if (loginResult.isCancelled) { throw new Error("Canceled by user"); } const accessToken = await AccessToken.getCurrentAccessToken(); if (!accessToken) { throw new Error("No access token"); } _authResponse = { userID: accessToken.userID, // FIXME: RNFBSDK bug: userId -> userID accessToken: accessToken.accessToken, expiresIn: Math.round((accessToken.expirationTime - Date.now()) / 1000) }; return _authResponse; } const FacebookSDK = { init() { // This is needed by Parse window.FB = FacebookSDK; }, login(callback: LoginCallback, options: LoginOptions) { loginWithFacebookSDK(options).then( authResponse => callback({ authResponse }), error => callback({ error }) ); }, getAuthResponse(): ?AuthResponse { return _authResponse; }, logout() { LoginManager.logOut(); }, /** * Make a API call to Graph server. This is the **real** RESTful API. * * Except the path, all arguments to this function are optional. So any of * these are valid: * * FB.api('/me') // throw away the response * FB.api('/me', function(r) { console.log(r) }) * FB.api('/me', { fields: 'email' }); // throw away response * FB.api('/me', { fields: 'email' }, function(r) { console.log(r) }); * FB.api('/12345678', 'delete', function(r) { console.log(r) }); * FB.api( * '/me/feed', * 'post', * { body: 'hi there' }, * function(r) { console.log(r) } * ); * * param path {String} the url path * param method {String} the http method * param params {Object} the parameters for the query * param cb {Function} the callback function to handle the response */ api: function(path: string, ...args: Array) { const argByType = {}; args.forEach(arg => { argByType[typeof arg] = arg; }); const httpMethod = (argByType.string || "get").toUpperCase(); const params = argByType.object || {}; const callback = argByType.function || emptyFunction; // FIXME: Move this into RNFBSDK // GraphRequest requires all parameters to be in {string: 'abc'} // or {uri: 'xyz'} format const parameters = mapObject(params, value => ({ string: value })); function processResponse(error, result) { // FIXME: RNFBSDK bug: result is Object on iOS and string on Android if (!error && typeof result === "string") { try { result = JSON.parse(result); } catch (e) { error = e; } } const data = error ? { error } : result; callback(data); } const request = new GraphRequest( path, { parameters, httpMethod }, processResponse ); new GraphRequestManager().addRequest(request).start(); } }; module.exports = FacebookSDK; ================================================ FILE: js/Playground.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import View from "View"; class Playground extends React.Component { state: { content: Array }; constructor() { super(); const content = []; const define = (name: string, render: Function) => { content.push(); }; // const Module = require('F8PageControl'); const Module = require("F8Header"); // const Module = require('./tabs/schedule/AddToScheduleButton'); // const Module = require('./rating/Header'); // $FlowFixMe: doesn't understand static Module.__cards__(define); this.state = { content }; } render() { return ( {this.state.content} ); } } class Example extends React.Component { state = { inner: null }; render() { const content = this.props.render(this.state.inner, inner => this.setState({ inner }) ); return {content}; } } module.exports = Playground; ================================================ FILE: js/PushNotificationsController.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import unseenNotificationsCount from "./tabs/notifications/unseenNotificationsCount"; import { AppState, Platform, PushNotificationIOS } from "react-native"; // $FlowIssue import PushNotification from "react-native-push-notification"; import { connect } from "react-redux"; import { storeDeviceToken, receivePushNotification, updateInstallation, markAllNotificationsAsSeen } from "./actions"; import type { Dispatch } from "./actions/types"; import { gcmSenderId } from "./env"; class AppBadgeController extends React.Component { props: { tab: string, enabled: boolean, badge: number, dispatch: Dispatch }; handleAppStateChange = appState => { if (appState === "active") { this.updateAppBadge(); if (this.props.tab === "info") { this.eventuallyMarkNotificationsAsSeen(); } } }; componentDidMount() { AppState.addEventListener("change", this.handleAppStateChange); const { dispatch } = this.props; PushNotification.configure({ onRegister: ({ token }) => dispatch(storeDeviceToken(token)), onNotification: notif => dispatch(receivePushNotification(notif)), senderID: gcmSenderId, requestPermissions: this.props.enabled }); this.updateAppBadge(); } componentWillUnmount() { AppState.removeEventListener("change", this.handleAppStateChange); } componentDidUpdate(prevProps) { if (!prevProps.enabled && this.props.enabled) { PushNotification.requestPermissions(); } if (this.props.badge !== prevProps.badge) { this.updateAppBadge(); } if (this.props.tab === "info" && prevProps.tab !== "info") { this.eventuallyMarkNotificationsAsSeen(); } } updateAppBadge() { if (this.props.enabled && Platform.OS === "ios") { PushNotificationIOS.setApplicationIconBadgeNumber(this.props.badge); updateInstallation({ badge: this.props.badge }); } } eventuallyMarkNotificationsAsSeen() { const { dispatch } = this.props; setTimeout(() => dispatch(markAllNotificationsAsSeen()), 1000); } render() { return null; } } function select(store) { return { enabled: store.notifications.enabled === true, badge: unseenNotificationsCount(store), tab: store.navigation.tab }; } module.exports = connect(select)(AppBadgeController); ================================================ FILE: js/actions/config.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import Parse from "parse/react-native"; import { InteractionManager } from "react-native"; import type { Action } from "./types"; async function loadConfig(): Promise { const config = await Parse.Config.get(); await InteractionManager.runAfterInteractions(); return { type: "LOADED_CONFIG", config }; } module.exports = { loadConfig }; ================================================ FILE: js/actions/filter.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import type { Action } from "./types"; type Schedule = { [key: string]: boolean }; type Video = { [key: string]: boolean }; function applyScheduleFilter(scheduleTopics: Schedule): Action { return { type: "APPLY_SCHEDULE_TOPICS_FILTER", scheduleTopics }; } function clearScheduleFilter(): Action { return { type: "CLEAR_SCHEDULE_FILTER" }; } function applyVideoFilter(videoTopics: Video): Action { return { type: "APPLY_VIDEO_TOPICS_FILTER", videoTopics }; } function clearVideoFilter(): Action { return { type: "CLEAR_VIDEO_FILTER" }; } module.exports = { applyScheduleFilter, clearScheduleFilter, applyVideoFilter, clearVideoFilter }; ================================================ FILE: js/actions/index.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import * as parseActions from "./parse"; import * as navigationActions from "./navigation"; import * as loginActions from "./login"; import * as scheduleActions from "./schedule"; import * as filterActions from "./filter"; import * as notificationActions from "./notifications"; import * as configActions from "./config"; import * as surveyActions from "./surveys"; import * as testActions from "./test"; import * as installationActions from "./installation"; import * as videoActions from "./video"; module.exports = { ...loginActions, ...scheduleActions, ...filterActions, ...notificationActions, ...configActions, ...surveyActions, ...testActions, ...parseActions, ...navigationActions, ...installationActions, ...videoActions }; ================================================ FILE: js/actions/installation.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { Platform } from "react-native"; import Parse from "parse/react-native"; async function currentInstallation(): Promise { const installationId = await Parse._getInstallationId(); return new Parse.Installation({ installationId, appName: "F8", deviceType: Platform.OS, // TODO: Get this information from the app itself appIdentifier: Platform.OS === "ios" ? "com.parse.f8" : "com.facebook.f8" }); } async function updateInstallation(updates: Object = {}): Promise { const installation = await currentInstallation(); await installation.save(updates); } module.exports = { currentInstallation, updateInstallation }; ================================================ FILE: js/actions/login.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Parse from "parse/react-native"; import FacebookSDK from "../FacebookSDK"; import ActionSheetIOS from "ActionSheetIOS"; import { Platform } from "react-native"; import Alert from "Alert"; import { restoreSchedule, loadFriendsSchedules } from "./schedule"; import { updateInstallation } from "./installation"; import { loadSurveys } from "./surveys"; import type { Action, ThunkAction } from "./types"; async function ParseFacebookLogin(scope): Promise { return new Promise((resolve, reject) => { Parse.FacebookUtils.logIn(scope, { success: resolve, error: (user, error) => reject((error && error.error) || error) }); }); } async function queryFacebookAPI(path, ...args): Promise { return new Promise((resolve, reject) => { FacebookSDK.api(path, ...args, response => { if (response && !response.error) { resolve(response); } else { reject(response && response.error); } }); }); } async function _logInWithFacebook(source: ?string): Promise> { await ParseFacebookLogin("public_profile,email,user_friends"); const profile = await queryFacebookAPI("/me", { fields: "name,email,link" }); const user = await Parse.User.currentAsync(); user.set("facebook_id", profile.id); user.set("name", profile.name); user.set("email", profile.email); user.set("link", profile.link); await user.save(); await updateInstallation({ user }); const action = { type: "LOGGED_IN", source, data: { id: profile.id, name: profile.name, sharedSchedule: user.get("sharedSchedule") } }; return Promise.all([Promise.resolve(action), restoreSchedule()]); } function logInWithFacebook(source: ?string): ThunkAction { return dispatch => { const login = _logInWithFacebook(source); // Loading friends schedules shouldn't block the login process login.then(result => { dispatch(result); dispatch(loadFriendsSchedules()); dispatch(loadSurveys()); }); return login; }; } function skipLogin(): Action { return { type: "SKIPPED_LOGIN" }; } function logOut(): ThunkAction { return dispatch => { Parse.User.logOut(); FacebookSDK.logout(); updateInstallation({ user: null, channels: [] }); // TODO: Make sure reducers clear their state return dispatch({ type: "LOGGED_OUT" }); }; } function logOutWithPrompt(): ThunkAction { return (dispatch, getState) => { let name = getState().user.name || "there"; if (Platform.OS === "ios") { ActionSheetIOS.showActionSheetWithOptions( { title: `Hi, ${name}`, options: ["Log out", "Cancel"], destructiveButtonIndex: 0, cancelButtonIndex: 1 }, buttonIndex => { if (buttonIndex === 0) { dispatch(logOut()); } } ); } else { Alert.alert(`Hi, ${name}`, "Log out from F8?", [ { text: "Cancel" }, { text: "Log out", onPress: () => dispatch(logOut()) } ]); } }; } module.exports = { logInWithFacebook, skipLogin, logOut, logOutWithPrompt }; ================================================ FILE: js/actions/navigation.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import type { Action } from "./types"; type Tab = "schedule" | "myF8" | "demos" | "videos" | "info"; module.exports = { switchTab: (tab: Tab): Action => ({ type: "SWITCH_TAB", tab }), switchDay: (day: 1 | 2): Action => ({ type: "SWITCH_DAY", day }) }; ================================================ FILE: js/actions/notifications.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { Platform, VibrationIOS } from "react-native"; import { updateInstallation } from "./installation"; import { loadNotifications } from "./parse"; import { loadSurveys } from "./surveys"; import { switchTab } from "./navigation"; import type { Action, ThunkAction } from "./types"; type PushNotification = { foreground: boolean, message: string, // react-native-push-notification library sends Object as data // on iOS and JSON string on android // TODO: Send PR to remove this inconsistency data: string | Object }; function normalizeData(s: string | Object): Object { if (s && typeof s === "object") { return s; } try { return JSON.parse(s); } catch (e) { return {}; } } async function storeDeviceToken(deviceToken: string): Promise { const pushType = Platform.OS === "android" ? "gcm" : undefined; await updateInstallation({ pushType, deviceToken, deviceTokenLastModified: Date.now() }); return { type: "REGISTERED_PUSH_NOTIFICATIONS" }; } function turnOnPushNotifications(): Action { return { type: "TURNED_ON_PUSH_NOTIFICATIONS" }; } function skipPushNotifications(): Action { return { type: "SKIPPED_PUSH_NOTIFICATIONS" }; } function receivePushNotification(notification: PushNotification): ThunkAction { return dispatch => { const { foreground, message } = notification; const data = normalizeData(notification.data); if (!foreground) { dispatch(switchTab("info")); } if (foreground) { dispatch(loadNotifications()); dispatch(loadSurveys()); if (Platform.OS === "ios") { VibrationIOS.vibrate(); } } if (data.e /* ephemeral */) { return; } const timestamp = new Date().getTime(); dispatch({ type: "RECEIVED_PUSH_NOTIFICATION", notification: { text: message, url: data.url, urlTitle: data.urlTitle, image: data.image, video: data.video, time: timestamp } }); }; } function markAllNotificationsAsSeen(): Action { return { type: "SEEN_ALL_NOTIFICATIONS" }; } module.exports = { turnOnPushNotifications, storeDeviceToken, skipPushNotifications, receivePushNotification, markAllNotificationsAsSeen }; ================================================ FILE: js/actions/parse.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import Parse from "parse/react-native"; import { logError, InteractionManager } from "react-native"; import type { ThunkAction } from "./types"; const Maps = Parse.Object.extend("Maps"); const Notification = Parse.Object.extend("Notification"); const FAQ = Parse.Object.extend("FAQ"); const Page = Parse.Object.extend("Page"); const Video = Parse.Object.extend("Video"); const Policy = Parse.Object.extend("Policy"); function loadParseQuery(type: string, query: Parse.Query): ThunkAction { return dispatch => { return query.find({ success: list => { // We don't want data loading to interfere with smooth animations InteractionManager.runAfterInteractions(() => { // Flow can't guarantee {type, list} is a valid action dispatch(({ type, list }: any)); }); }, error: logError }); }; } function loadSessions(): ThunkAction { return loadParseQuery( "LOADED_SESSIONS", new Parse.Query("Agenda").include("speakers").ascending("startTime") ); } function loadMaps(): ThunkAction { return loadParseQuery("LOADED_MAPS", new Parse.Query(Maps)); } function loadNotifications(): ThunkAction { return loadParseQuery("LOADED_NOTIFICATIONS", new Parse.Query(Notification)); } function loadFAQs(): ThunkAction { return loadParseQuery( "LOADED_FAQS", new Parse.Query(FAQ).ascending("updatedAt") ); } function loadPages(): ThunkAction { return loadParseQuery( "LOADED_PAGES", new Parse.Query(Page).ascending("title") ); } function loadVideos(): ThunkAction { return loadParseQuery( "LOADED_VIDEOS", new Parse.Query(Video).descending("updatedAt") ); } function loadPolicies(): ThunkAction { return loadParseQuery( "LOADED_POLICIES", new Parse.Query(Policy).ascending("title") ); } export { loadSessions, loadMaps, loadNotifications, loadFAQs, loadPages, loadVideos, loadPolicies }; ================================================ FILE: js/actions/schedule.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import Parse from "parse/react-native"; import F8Analytics from "../F8Analytics"; import { Platform, InteractionManager, ActionSheetIOS, Alert, Share } from "react-native"; const Agenda = Parse.Object.extend("Agenda"); import { currentInstallation, updateInstallation } from "./installation"; import type { ThunkAction, PromiseAction, Dispatch } from "./types"; import type { Session } from "../reducers/sessions"; function addToSchedule(id: string): ThunkAction { return (dispatch: Dispatch) => { if (Parse.User.current()) { Parse.User .current() .relation("mySchedule") .add(new Agenda({ id })); Parse.User.current().save(); currentInstallation().then(installation => { installation.addUnique("channels", `session_${id}`); return installation.save(); }); } dispatch({ type: "SESSION_ADDED", id }); }; } function removeFromSchedule(id: string): ThunkAction { return (dispatch: Dispatch) => { if (Parse.User.current()) { Parse.User .current() .relation("mySchedule") .remove(new Agenda({ id })); Parse.User.current().save(); currentInstallation().then(installation => { installation.remove("channels", `session_${id}`); return installation.save(); }); } dispatch({ type: "SESSION_REMOVED", id }); }; } function removeFromScheduleWithPrompt(session: Session): ThunkAction { return dispatch => { if (Platform.OS === "ios") { ActionSheetIOS.showActionSheetWithOptions( { options: ["Remove From Schedule", "Cancel"], destructiveButtonIndex: 0, cancelButtonIndex: 1 }, buttonIndex => { if (buttonIndex === 0) { dispatch(removeFromSchedule(session.id)); } } ); } else { Alert.alert( "Remove From Your Schedule", `Would you like to remove "${session.title}" from your schedule?`, [ { text: "Cancel" }, { text: "Remove", onPress: () => dispatch(removeFromSchedule(session.id)) } ] ); } }; } async function restoreSchedule(): PromiseAction { const list = await Parse.User .current() .relation("mySchedule") .query() .find(); const channels = list.map(({ id }) => `session_${id}`); updateInstallation({ channels }); return { type: "RESTORED_SCHEDULE", list }; } async function loadFriendsSchedules(): PromiseAction { const list = await Parse.Cloud.run("friends"); await InteractionManager.runAfterInteractions(); return { type: "LOADED_FRIENDS_SCHEDULES", list }; } function setSharingEnabled(enabled: boolean): ThunkAction { return dispatch => { dispatch({ type: "SET_SHARING", enabled }); Parse.User.current().set("sharedSchedule", enabled); Parse.User.current().save(); }; } function shareSession(session: Session): ThunkAction { return (dispatch, getState) => { const { sessionURLTemplate } = getState().config; const url = sessionURLTemplate .replace("{slug}", session.slug) .replace("{id}", session.id); if (Platform.OS === "ios") { ActionSheetIOS.showShareActionSheetWithOptions( { message: session.title, url }, e => {}, logShare.bind(null, session.id) ); } else { Share.share( { // content title: session.title, message: url }, { // options dialogTitle: "Share Link to " + session.title // droid-only share option } ).then( // callback _ => logShare(session.id, true, null) ); } }; } function logShare(id, completed, activity) { F8Analytics.logEvent("Share Session", 1, { id }); // Parse.Analytics.track('share', { // id, // completed: completed ? 'yes' : 'no', // activity: activity || '?' // }); } module.exports = { shareSession, addToSchedule, removeFromSchedule, restoreSchedule, loadFriendsSchedules, setSharingEnabled, removeFromScheduleWithPrompt }; ================================================ FILE: js/actions/surveys.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import Parse from "parse/react-native"; import type { Action } from "./types"; async function loadSurveys(): Promise { const list = await Parse.Cloud.run("surveys"); return { type: "LOADED_SURVEYS", list }; } async function submitSurveyAnswers( id: string, answers: Array ): Promise { await Parse.Cloud.run("submit_survey", { id, answers }); return { type: "SUBMITTED_SURVEY_ANSWERS", id }; } module.exports = { loadSurveys, submitSurveyAnswers }; ================================================ FILE: js/actions/test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Parse from "parse/react-native"; import { Alert, ActionSheetIOS, Platform } from "react-native"; import { version } from "../env"; import type { Action, ThunkAction } from "./types"; function testPlainPush(): ThunkAction { return () => Parse.Cloud.run("test_push"); } function testLinkPush(): ThunkAction { return () => Parse.Cloud.run("test_push", { url: "link" }); } function testSessionPush(): ThunkAction { return () => Parse.Cloud.run("test_push", { url: "session" }); } function testImagePush(): ThunkAction { return () => Parse.Cloud.run("test_push", { url: "image" }); } function testVideoPush(): ThunkAction { return () => Parse.Cloud.run("test_push", { url: "video" }); } function testSurveyPush(): ThunkAction { return () => Parse.Cloud.run("test_survey"); } function testResetNuxes(): Action { return { type: "RESET_NUXES" }; } function getAppInfo(): ThunkAction { return (dispatch, getState) => { const subject = `App v${version} state`; const message = JSON.stringify(getState(), undefined, 2); if (Platform.OS === "ios") { ActionSheetIOS.showShareActionSheetWithOptions( { subject: subject, message: message }, () => {}, () => {} ); } else { Alert.alert(subject); } }; } function testSetCurrentDate(value: ?number): Action { return { type: "SET_TIMED_TESTING", value }; } const TEST_MENU = { "Request a push notification": testPlainPush, "Push with link": testLinkPush, "Push with session": testSessionPush, "Push with image": testImagePush, "Push with video": testVideoPush, "Request a survey": testSurveyPush, "Reset NUXes": testResetNuxes, "Get app info": getAppInfo, "Set current date: Day 1": _ => testSetCurrentDate(1), "Set current date: Day 2": _ => testSetCurrentDate(2), "Reset current date": _ => testSetCurrentDate(null) }; module.exports = { TEST_MENU }; ================================================ FILE: js/actions/types.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; type ParseObject = Object; export type Action = | { type: "LOADED_ABOUT", list: Array } | { type: "LOADED_NOTIFICATIONS", list: Array } | { type: "LOADED_MAPS", list: Array } | { type: "LOADED_FRIENDS_SCHEDULES", list: Array<{ id: string, name: string, schedule: { [key: string]: boolean } }> } | { type: "LOADED_CONFIG", config: ParseObject } | { type: "LOADED_SESSIONS", list: Array } | { type: "LOADED_SURVEYS", list: Array } | { type: "SUBMITTED_SURVEY_ANSWERS", id: string } | { type: "LOGGED_IN", source: ?string, data: { id: string, name: string, sharedSchedule: ?boolean } } | { type: "RESTORED_SCHEDULE", list: Array } | { type: "SKIPPED_LOGIN" } | { type: "LOGGED_OUT" } | { type: "SESSION_ADDED", id: string } | { type: "SESSION_REMOVED", id: string } | { type: "SET_SHARING", enabled: boolean } | { type: "APPLY_TOPICS_FILTER", topics: { [key: string]: boolean } } | { type: "CLEAR_FILTER" } | { type: "SWITCH_DAY", day: 1 | 2 } | { type: "SWITCH_TAB", tab: "schedule" | "my-schedule" | "map" | "notifications" | "info" } | { type: "TURNED_ON_PUSH_NOTIFICATIONS" } | { type: "REGISTERED_PUSH_NOTIFICATIONS" } | { type: "SKIPPED_PUSH_NOTIFICATIONS" } | { type: "RECEIVED_PUSH_NOTIFICATION", notification: Object } | { type: "SEEN_ALL_NOTIFICATIONS" } | { type: "RESET_NUXES" }; export type Dispatch = ( action: Action | ThunkAction | PromiseAction | Array ) => any; export type GetState = () => Object; export type ThunkAction = (dispatch: Dispatch, getState: GetState) => any; export type PromiseAction = Promise; ================================================ FILE: js/actions/video.js ================================================ import F8Analytics from "../F8Analytics"; import { Platform, Share, ActionSheetIOS } from "react-native"; import type { ThunkAction } from "./types"; import type { Video } from "../reducers/videos"; function shareVideo(video: Video): ThunkAction { return dispatch => { if (Platform.OS === "ios") { ActionSheetIOS.showShareActionSheetWithOptions( { message: video.title, url: video.shareURL }, e => {}, logShare.bind(null, video.id) ); } else { Share.share( { // content title: video.title, message: video.shareURL }, { // options dialogTitle: "Share Link to " + video.title // droid-only share option } ).then( // callback _ => logShare(video.id, true, null) ); } }; } function logShare(id, completed, activity) { F8Analytics.logEvent("Share Video", 1, { id }); // Parse.Analytics.track('share', { // id, // completed: completed ? 'yes' : 'no', // activity: activity || '?' // }); } export { shareVideo }; ================================================ FILE: js/common/ActionsOverlay.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import { View, Alert } from "react-native"; import LinearGradient from "react-native-linear-gradient"; import StyleSheet from "./F8StyleSheet"; import F8Colors from "./F8Colors"; import F8Button from "./F8Button"; /* config =================================================================== */ const CONTAINER_HEIGHT = 126, BUTTON_PADDING_H = 17, BUTTON_PADDING_B = 27; /* ============================================================================= ============================================================================= */ class ActionsOverlay extends React.Component { static __cards__; static height = CONTAINER_HEIGHT; static defaultProps = { buttonContainerStyles: { paddingHorizontal: BUTTON_PADDING_H, paddingBottom: BUTTON_PADDING_B }, gradientColors: [F8Colors.colorWithAlpha("tangaroa", 0), F8Colors.tangaroa], gradientStart: { x: 0.5, y: 0 }, gradientEnd: { x: 0.5, y: 1 } }; render() { const { children, gradientColors, gradientStart, gradientEnd, buttonContainerStyles } = this.props; return ( {children} ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { // position: 'absolute', height: CONTAINER_HEIGHT, // flex:1, justifyContent: "flex-end", alignItems: "center" }, gradient: { position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }, buttonContainer: { flexDirection: "row" } }); /* Playground Cards ============================================================================= */ const actionsOverlay = ActionsOverlay; actionsOverlay.__cards__ = define => { define("Default", () => ( Alert.alert("round (white) pressed!")} /> Alert.alert("round (blue) pressed!")} /> )); define("Session Details", () => ( Alert.alert("button pressed!")} /> Alert.alert("round (blue) pressed!")} /> )); }; /* Export ============================================================================= */ module.exports = actionsOverlay; ================================================ FILE: js/common/Carousel.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import ViewPager from "./ViewPager"; import { Dimensions } from "react-native"; type Props = { count: number, selectedIndex: number, onSelectedIndexChange?: (index: number) => void, renderCard: (index: number) => ReactElement, style?: any }; const WINDOW_WIDTH = Dimensions.get("window").width; class Carousel extends React.Component { props: Props; static CardWidth = WINDOW_WIDTH; render() { let cards = []; const { count, selectedIndex, renderCard } = this.props; for (let i = 0; i < count; i++) { let content = null; if (Math.abs(i - selectedIndex) < 2) { content = renderCard(i); } cards.push(content); } return ( {cards} ); } } module.exports = Carousel; ================================================ FILE: js/common/F8ActionSheet.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { Modal, Animated, View, Alert } from "react-native"; import LinearGradient from "react-native-linear-gradient"; import { Text } from "./F8Text"; import StyleSheet from "./F8StyleSheet"; import F8Colors from "./F8Colors"; import F8Button from "./F8Button"; import Hitbox from "Hitbox"; /* constants ================================================================ */ const GRADIENT_HEIGHT = 220, BUTTON_PADDING_T = 15, BUTTON_PADDING_H = 17, BUTTON_PADDING_B = 18, BUTTON_ANIM_DIST = 50, INTRO_ANIM_DUR = 180, OUTRO_ANIM_DUR = 150; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {Array} actions Action buttons to render { text:str, onPress:fn } * @param {function} onCancel 'x' button callback (called after outro animation) * @param {?string} title Actions header * @return {ReactElement} * ============================================================================== */ class F8ActionSheet extends React.Component { constructor() { super(); this.outro = this.outro.bind(this); this.state = { revealed: false, introTransition: new Animated.Value(0) }; this.animatedContainer = { opacity: this.state.introTransition }; this.animatedContent = { marginBottom: this.state.introTransition.interpolate({ inputRange: [0, 1], outputRange: [-BUTTON_ANIM_DIST, 0] }) }; Animated.timing(this.state.introTransition, { toValue: 1, duration: INTRO_ANIM_DUR }).start(); } render() { let actions = (this.props.actions || []).map((action, index) => this.renderAction(action, index) ); let title = this.props.title ? ( {this.props.title} ) : null; return ( this.outro(this.props.onCancel)} /> {title} {actions} {this.renderCancel()} ); } renderAction(obj, i) { return ( this.outro(obj.onPress)} /> ); } renderCancel() { return ( this.outro(this.props.onCancel)} /> ); } outro(cb) { Animated.timing(this.state.introTransition, { toValue: 0, duration: OUTRO_ANIM_DUR }).start(cb); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0, flex: 1, justifyContent: "flex-end", backgroundColor: F8Colors.colorWithAlpha("tangaroa", 0.5) }, gradient: { height: GRADIENT_HEIGHT }, buttonGroup: { paddingTop: BUTTON_PADDING_T, paddingHorizontal: BUTTON_PADDING_H, paddingBottom: BUTTON_PADDING_B, backgroundColor: F8Colors.colorWithAlpha("tangaroa", 0.8) }, actionBtn: { marginBottom: 10 }, cancelBtn: { alignSelf: "center", marginTop: 5 }, title: { color: F8Colors.white, fontSize: 18, textAlign: "center", padding: 20 } }); /* playground cards ========================================================= */ const actionSheet = F8ActionSheet; actionSheet.__cards__ = define => { const exampleActions = [ { text: "Action 1", onPress: () => Alert.alert("Do action 1!") }, { text: "Action 2", onPress: () => Alert.alert("Do action 2!") } ]; define("Default", () => ( Alert.alert("Cancel")} /> )); }; /* exports ================================================================== */ module.exports = actionSheet; ================================================ FILE: js/common/F8BackgroundRepeat.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import { View, Image } from "react-native"; import resolveAssetSource from "resolveAssetSource"; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {number} source ReactNative asset source * @param {number} width repeating container width * @param {number} height repeating container height * @return {ReactElement} * ============================================================================== */ class F8BackgroundRepeat extends React.Component { static __cards__; render() { const { source, width, height } = this.props; const img = resolveAssetSource(source); const content = []; const numHorizontal = Math.ceil(width / img.width); const numVertical = Math.ceil(height / img.height); for (let i = 0; i < numVertical; i++) { content.push(this.renderRow(numHorizontal, i)); } return ( {content} ); } renderRow(colsInRow: number, idx: number) { const cols = []; for (let i = 0; i < colsInRow; i++) { cols.push(this.renderImage(i)); } return ( {cols} ); } renderImage(idx: number) { return ; } } /* Playground Cards ========================================================= */ const backgroundRepeat = F8BackgroundRepeat; backgroundRepeat.__cards__ = define => { define("Back Buttons", _ => ( )); define("F8 Logos", _ => ( )); define("Dot Pattern", _ => ( )); }; /* exports ================================================================== */ module.exports = backgroundRepeat; ================================================ FILE: js/common/F8Button.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import F8Fonts from "./F8Fonts"; import { View, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; import { Text } from "./F8Text"; /* constants ================================================================ */ const BUTTON_HEIGHT = 52, BUTTON_HEIGHT_SM = 32; /* ============================================================================= */ class F8Button extends React.Component { props: { theme: | "pink" | "blue" | "yellow" | "fb" | "white" | "bordered" | "bordered-pink" | "disabled", type: "default" | "round" | "small", opacity: number, icon?: number, caption?: string, style?: any, fontSize?: number, // buttonColor?: string; // contentColor?: string; onPress: () => mixed }; static defaultProps = { opacity: 1, theme: "pink" }; static height = BUTTON_HEIGHT; render() { const { icon, fontSize, opacity } = this.props; const caption = this.props.caption && this.props.caption.toUpperCase(); const { buttonTheme, iconTheme, captionTheme } = this.getTheme(); const { containerType, buttonType, iconType, captionType } = this.getType(); let iconImage; if (icon) { iconImage = ( ); } let fontSizeOverride; if (fontSize) { fontSizeOverride = { fontSize }; } const content = ( {iconImage} {caption} ); if (this.props.onPress) { return ( {content} ); } else { return ( {content} ); } } getTheme() { const { theme } = this.props; let buttonTheme, iconTheme, captionTheme; if (theme === "yellow") { buttonTheme = { backgroundColor: F8Colors.yellow }; iconTheme = { tintColor: F8Colors.pink }; captionTheme = { color: F8Colors.pink }; } else if (theme === "blue") { buttonTheme = { backgroundColor: F8Colors.blue }; iconTheme = { tintColor: F8Colors.white }; captionTheme = { color: F8Colors.white }; } else if (theme === "fb") { buttonTheme = { backgroundColor: F8Colors.facebookBlue }; iconTheme = { tintColor: F8Colors.white }; captionTheme = { color: F8Colors.white }; } else if (theme === "white") { buttonTheme = { backgroundColor: F8Colors.white }; iconTheme = { tintColor: F8Colors.pink }; captionTheme = { color: F8Colors.pink }; } else if (theme === "bordered") { buttonTheme = { backgroundColor: "transparent", borderWidth: 1, borderColor: F8Colors.tangaroa }; iconTheme = { tintColor: F8Colors.tangaroa }; captionTheme = { color: F8Colors.tangaroa }; } else if (theme === "bordered-pink") { buttonTheme = { backgroundColor: "transparent", borderWidth: 1, borderColor: F8Colors.pink }; iconTheme = { tintColor: F8Colors.pink }; captionTheme = { color: F8Colors.pink }; } else if (theme === "maps") { buttonTheme = { backgroundColor: "transparent", borderWidth: 1, borderColor: F8Colors.purple }; iconTheme = { tintColor: F8Colors.tangaroa }; captionTheme = { color: F8Colors.tangaroa }; } else if (theme === "mapsInactive") { buttonTheme = { backgroundColor: "transparent", borderWidth: 1, borderColor: "transparent" }; iconTheme = { tintColor: F8Colors.tangaroa }; captionTheme = { color: F8Colors.tangaroa }; } else if (theme === "disabled") { buttonTheme = { backgroundColor: F8Colors.blueBayoux }; iconTheme = { tintColor: F8Colors.white, opacity: 0.5 }; captionTheme = { color: F8Colors.white, opacity: 0.5 }; } else if (theme === "transparent") { buttonTheme = { backgroundColor: "transparent" }; iconTheme = { tintColor: F8Colors.tangaroa }; captionTheme = { color: F8Colors.tangaroa }; } else { // pink/white is default buttonTheme = { backgroundColor: F8Colors.pink }; iconTheme = { tintColor: "white" }; captionTheme = { color: "white" }; } return { buttonTheme, iconTheme, captionTheme }; } getType() { const { type } = this.props; let containerType, buttonType, iconType, captionType; if (type === "round") { buttonType = { width: BUTTON_HEIGHT, paddingHorizontal: 0 }; iconType = { marginRight: 0 }; captionType = { fontSize: 13 }; } else if (type === "small") { containerType = { height: BUTTON_HEIGHT_SM }; buttonType = { paddingHorizontal: 15 }; iconType = { marginRight: 0 }; captionType = { fontSize: 13 }; } else { // defaults } return { containerType, buttonType, iconType, captionType }; } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { height: BUTTON_HEIGHT }, button: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", paddingHorizontal: 30, borderRadius: BUTTON_HEIGHT / 2 }, buttonRound: { width: BUTTON_HEIGHT, paddingHorizontal: 0 }, icon: { marginRight: 12 }, caption: { fontFamily: F8Fonts.button, fontSize: 15, textAlign: "center" } }); /* Playground Cards ============================================================================= */ const Button = F8Button; Button.__cards__ = define => { define("default (pink)", () => ( Alert.alert("default (pink) pressed!")} /> )); define("blue", () => ( Alert.alert("blue pressed!")} /> )); define("yellow (w/ icon)", () => ( Alert.alert("yellow (icon) pressed!")} /> )); define("fb", () => ( Alert.alert("fb pressed!")} /> )); define("white", () => ( Alert.alert("white pressed!")} /> )); define("bordered", () => ( Alert.alert("bordered pressed!")} /> )); define("bordered-pink", () => ( Alert.alert("bordered-pink pressed!")} /> )); define("round (caption)", () => ( Alert.alert("round (caption) pressed!")} /> )); define("round (white)", () => ( Alert.alert("round (white) pressed!")} /> )); define("round (blue)", () => ( Alert.alert("round (blue) pressed!")} /> )); define("fixed width", () => ( Alert.alert("round (blue) pressed!")} /> )); define("small (blue)", () => ( Alert.alert("small (maps) pressed!")} /> )); }; /* Exports ============================================================================= */ module.exports = Button; ================================================ FILE: js/common/F8Colors.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; /* Color Definitions ============================================================================= */ const NAMED_COLORS = { // grayscale (light to dark) white: "rgba(255, 255, 255, 1)", bianca: "rgba(251, 249, 240, 1)", timberwolf: "rgba(218, 216, 210, 1)", magnesium: "rgba(178, 178, 178, 1)", black: "rgba(3, 3, 3, 1)", // blues (light to dark) iceberg: "rgba(216, 240, 246, 1)", coolGray: "rgba(136, 145, 181, 1)", blueBayoux: "rgba(101, 113, 135, 1)", facebookBlue: "rgba(66, 103, 178, 1)", blue: "rgba(29, 86, 251, 1)", palatinateBlue: "rgba(24, 76, 223, 1)", persianBlue: "rgba(23, 68, 200, 1)", sapphire: "rgba(10, 42, 102, 1)", sapphire2: "rgba(18, 36, 108, 1)", tangaroa: "rgba(1, 23, 65, 1)", blueCharcoal: "rgba(1, 10, 28, 1)", // the rest yellow: "rgba(246, 253, 55, 1)", green: "rgba(106, 246, 162, 1)", turquoise: "rgba(0, 205, 223, 1)", purple: "rgba(144, 63, 199, 1)", pink: "rgba(245, 64, 199, 1)", darkPink: "rgba(200, 40, 159, 1)", orange: "rgba(247, 144, 77, 1)", salmon: "rgba(243, 91, 89, 1)" }; const THEME_COLORS = { // pass through for use with colorWithAlpha ...NAMED_COLORS, // alias the named colors by use-case actionText: NAMED_COLORS.blue, lightBackground: NAMED_COLORS.bianca, darkBackground: NAMED_COLORS.blueCharcoal, darkText: NAMED_COLORS.blueCharcoal, cellBorder: NAMED_COLORS.blueCharcoal, lightText: NAMED_COLORS.blueBayoux, // legacy inactiveText: "#9B9B9B" }; const LOCATION_COLORS = { "220A": NAMED_COLORS.sapphire2, "220B": NAMED_COLORS.purple, "220C": NAMED_COLORS.blue, "210F": NAMED_COLORS.turquoise, "210G": NAMED_COLORS.turquoise, LL20: NAMED_COLORS.green, REGISTRATION: NAMED_COLORS.tangaroa, REGISTRATIONDESK: NAMED_COLORS.tangaroa, FESTIVALHALL: NAMED_COLORS.sapphire2, CITYNATIONALCIVIC: NAMED_COLORS.pink, A: NAMED_COLORS.sapphire2, B: NAMED_COLORS.purple, C: NAMED_COLORS.blue, F: NAMED_COLORS.turquoise, G: NAMED_COLORS.turquoise, LL: NAMED_COLORS.green }; /* Exports ============================================================================= */ module.exports = { ...THEME_COLORS, // pass through all theme colors (named and by-purpose) colorWithAlpha(name: string = "blue", opacity: number = 1) { if (!THEME_COLORS[name]) { name = "blue"; } return THEME_COLORS[name].split(", 1)").join(`, ${opacity})`); }, colorForLocation(location: ?string): string { if (!location) { return NAMED_COLORS.tangaroa; } let color = LOCATION_COLORS[location.replace(/ /g, "").toUpperCase()]; if (!color) { color = NAMED_COLORS.tangaroa; } return color; }, colorForTopic(count: number, index: number): string { const hue = Math.round(360 * index / (count + 1)); return `hsl(${hue}, 74%, 65%)`; } }; ================================================ FILE: js/common/F8DrawerLayout.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { DrawerLayoutAndroid } from "react-native"; class F8DrawerLayout extends React.Component { _drawer: ?DrawerLayoutAndroid; constructor(props: any, context: any) { super(props, context); (this: any).openDrawer = this.openDrawer.bind(this); (this: any).closeDrawer = this.closeDrawer.bind(this); (this: any).onDrawerOpen = this.onDrawerOpen.bind(this); (this: any).onDrawerClose = this.onDrawerClose.bind(this); (this: any).handleBackButton = this.handleBackButton.bind(this); } render() { const { drawerPosition, ...props } = this.props; const { Right, Left } = DrawerLayoutAndroid.positions; return ( { this._drawer = drawer; }} {...props} drawerPosition={drawerPosition === "right" ? Right : Left} onDrawerOpen={this.onDrawerOpen} onDrawerClose={this.onDrawerClose} /> ); } componentWillUnmount() { this.context.removeBackButtonListener(this.handleBackButton); this._drawer = null; } handleBackButton(): boolean { this.closeDrawer(); return true; } onDrawerOpen() { this.context.addBackButtonListener(this.handleBackButton); this.props.onDrawerOpen && this.props.onDrawerOpen(); } onDrawerClose() { this.context.removeBackButtonListener(this.handleBackButton); this.props.onDrawerClose && this.props.onDrawerClose(); } closeDrawer() { this._drawer && this._drawer.closeDrawer(); } openDrawer() { this._drawer && this._drawer.openDrawer(); } } F8DrawerLayout.contextTypes = { addBackButtonListener: React.PropTypes.func, removeBackButtonListener: React.PropTypes.func }; module.exports = F8DrawerLayout; ================================================ FILE: js/common/F8Fonts.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import { Platform, Dimensions } from "react-native"; const DEVICE_SCALE = Dimensions.get("window").width / 375; const DEFAULT_FONT = "helvetica"; const SECONDARY_FONT = Platform.OS === "android" ? "basis" : "helvetica"; /* utils ==================================================================== */ // get font name and weight function fontWithWeight( family: string = DEFAULT_FONT, weight: string = "regular" ): string { return family; } function normalize(size: number): number { return Math.round(DEVICE_SCALE * size); } // attempt to normalize x-platform line heights function lineHeight( val: number = 1, scale: number = 1, normalized: boolean = true ): number { let adjusted = normalized ? normalize(val) : val; return Math.round(Platform.OS === "android" ? adjusted * scale : adjusted); } /* export =================================================================== */ export default { default: DEFAULT_FONT, helvetica: DEFAULT_FONT, basis: SECONDARY_FONT, h1: DEFAULT_FONT, h2: DEFAULT_FONT, h3: DEFAULT_FONT, h4: DEFAULT_FONT, p: DEFAULT_FONT, button: DEFAULT_FONT, fontWithWeight, lineHeight, normalize }; ================================================ FILE: js/common/F8Header.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { Dimensions, View, Image, ToolbarAndroid, Platform, TouchableOpacity, Alert } from "react-native"; import { Text, HeaderTitle } from "./F8Text"; import F8Colors from "./F8Colors"; import F8Fonts from "./F8Fonts"; import StyleSheet from "./F8StyleSheet"; /* Config ============================================================================= */ let STATUS_BAR_HEIGHT = Platform.OS === "ios" ? 20 : 25; if (Platform.OS === "android" && Platform.Version && Platform.Version < 21) { STATUS_BAR_HEIGHT = 0; } const HEADER_HEIGHT = Platform.OS === "ios" ? 45 + STATUS_BAR_HEIGHT : 60 + STATUS_BAR_HEIGHT; const SCREEN_WIDTH = Dimensions.get("window").width; const IOS_ITEM_TEXT_SIZE = SCREEN_WIDTH < 375 ? 10 : 13; const FAVORITE_ICON_WIDTH = 37, FAVORITE_ICON_HEIGHT = 31; export type Layout = | "default" // Use platform defaults (icon on Android, text on iOS) | "icon" // Always use icon | "title"; // Always use title export type Item = { title?: string, icon?: number, layout?: Layout, onPress?: () => void }; export type Props = { title?: string, leftItem?: Item, rightItem?: Item, extraItems?: Array, style?: any, children?: any }; /* ============================================================================= (When Platform.os is Android) -------------------------------------------------------------------------------- ToolbarAndroid header ============================================================================= */ class F8HeaderAndroid extends React.Component { static height: number; props: Props; static defaultProps = { backgroundColor: F8Colors.blue, titleColor: F8Colors.yellow, itemsColor: F8Colors.white }; constructor() { super(); this.limitActionSelection = false; } render() { const { navItem, leftItem, rightItem, extraItems, backgroundColor, titleColor } = this.props; let actions = []; if (leftItem) { const { title, icon, layout } = leftItem; actions.push({ icon: layout !== "title" ? icon : undefined, title: title, show: "always" }); } if (rightItem) { const { title, icon, layout } = rightItem; actions.push({ icon: layout !== "title" ? icon : undefined, title: title, show: "always" }); } if (extraItems) { actions = actions.concat( extraItems.map(item => ({ title: item.title, show: "never" })) ); } let content; if (React.Children.count(this.props.children) > 0) { content = ( {this.props.children} ); } else { content = ( {this.props.title} ); } return ( {content} {actions.length || 0} ); } handleActionSelected(position: number) { if (this.limitActionSelection) { return; } let items = this.props.extraItems || []; if (this.props.rightItem) { items = [this.props.rightItem, ...items]; } if (this.props.leftItem) { items = [this.props.leftItem, ...items]; } const item = items[position]; item && item.onPress && item.onPress(); this.limitActionSelection = true; setTimeout(() => { this.limitActionSelection = false; }, 1000); } } /* ============================================================================= (When Platform.os is iOS) -------------------------------------------------------------------------------- View header ============================================================================= */ class F8HeaderIOS extends React.Component { static height: number; props: Props; static defaultProps = { backgroundColor: F8Colors.blue, titleColor: F8Colors.yellow, itemsColor: F8Colors.white }; render() { const { navItem, leftItem, title, rightItem, backgroundColor, titleColor, itemsColor } = this.props; let left; if (navItem) { if (navItem.back) { navItem.icon = require("./img/header/back.png"); } left = ( ); } else { left = ; } const content = React.Children.count(this.props.children) === 0 ? ( {title} ) : ( this.props.children ); const right = ; return ( {left} {content} {right} ); } } /* ============================================================================= -------------------------------------------------------------------------------- Item wrapper ============================================================================= */ class ItemWrapperIOS extends React.Component { props: { item: Item, color: string }; render() { const { item, color } = this.props; if (!item) { return null; } let content; const { title, icon, layout, onPress } = item; if (layout !== "icon" && title) { content = ( {title.toUpperCase()} ); } else if (icon) { content = ; } return ( {content} ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ toolbar: { android: { // backgroundColor: F8Colors.background, height: HEADER_HEIGHT - STATUS_BAR_HEIGHT } }, header: { android: { paddingTop: STATUS_BAR_HEIGHT }, ios: { backgroundColor: "transparent", paddingTop: STATUS_BAR_HEIGHT, height: HEADER_HEIGHT, flexDirection: "row", justifyContent: "space-between", alignItems: "center" } }, titleTextDivider: { android: { position: "absolute", height: 1, bottom: 0, backgroundColor: F8Colors.accent } }, leftItem: { flex: 1, alignItems: "flex-start" }, centerItem: { flex: 2, alignItems: "center" }, rightItem: { flex: 1, alignItems: "flex-end" }, itemWrapper: { padding: 11 }, itemText: { fontFamily: F8Fonts.helvetica, fontSize: IOS_ITEM_TEXT_SIZE, color: F8Colors.highContrast }, favoriteIcon: { android: { position: "absolute", width: FAVORITE_ICON_WIDTH, height: FAVORITE_ICON_HEIGHT, top: HEADER_HEIGHT / 2 - FAVORITE_ICON_HEIGHT / 2 + STATUS_BAR_HEIGHT / 2, right: 0 } } }); /* Playground Cards ============================================================================= */ const Header = Platform.OS === "ios" ? F8HeaderIOS : F8HeaderAndroid; Header.height = HEADER_HEIGHT; // $FlowFixMe Header.__cards__ = define => { const navItem = { back: true, onPress: () => Alert.alert("Menu button pressed!") }; const favoritesItem = { title: "Favorites", icon: require("./img/header/star.png"), onPress: () => Alert.alert("Favorites button pressed!") }; const topicsItem = { title: "Topics", icon: require("./img/header/filter.png"), onPress: () => Alert.alert("Topics button pressed!") }; define("Simple", () =>
); define("Simple w/ Nav", () => (
)); define("With items", () => (
)); define("Forcing icons", () => (
)); define("Forcing title", () => (
)); define("With content", () => { const alignmentStyles = Platform.OS === "android" ? null : { textAlign: "center" }; return (
Wed 9/23 {"\n"} 12:00
); }); define("With Background", () => (
)); define("With light background", () => (
)); }; /* Export ============================================================================= */ module.exports = Header; ================================================ FILE: js/common/F8Linking.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { Linking } from "react-native"; const WHITELISTED_URL_SCHEMES = [ "https:", "http:", "mailto:", "comgooglemaps-x-callback:" ]; const ERR_NOT_LISTED = "F8Linking: URL does not match whitelisted schemes"; function allowed(source: string) { return !!WHITELISTED_URL_SCHEMES.find(p => source.indexOf(p) === 0); } export default class F8Linking { static async openURL(source: string) { if (!allowed(source)) { throw new Error(ERR_NOT_LISTED); } return Linking.openURL(source); } static async canOpenURL(source: string) { if (!allowed(source)) { return false; } return Linking.canOpenURL(source); } } ================================================ FILE: js/common/F8Modal.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { Modal, View, StyleSheet } from "react-native"; import LinearGradient from "react-native-linear-gradient"; /* Constants ============================================================================= */ const MODAL_PADDING_H = 10, MODAL_BORDER_RADIUS = 4, FOOTER_GRADIENT_HEIGHT = 126; /* ============================================================================= */ class F8Modal extends React.Component { static defaultProps = { transparent: true, animationType: "fade" }; render() { return ( {this.props.bottomGradient && this.props.bottomGradient.length === 2 ? this.renderBottomGradient() : null} {this.props.renderContent ? this.renderContent() : null} {this.props.renderFooter ? this.renderFooter() : null} ); } renderContent() { return {this.props.renderContent()}; } renderFooter() { return {this.props.renderFooter()}; } renderBottomGradient() { return ( ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.colorWithAlpha("tangaroa", 0.8), justifyContent: "center", alignItems: "center", paddingHorizontal: MODAL_PADDING_H }, card: { backgroundColor: F8Colors.white, borderRadius: MODAL_BORDER_RADIUS }, footer: { position: "absolute", left: 0, right: 0, bottom: 0, alignItems: "center" }, bottomGradient: { position: "absolute", left: 0, right: 0, bottom: 0, height: FOOTER_GRADIENT_HEIGHT } }); /* Exports ============================================================================= */ module.exports = F8Modal; ================================================ FILE: js/common/F8PageControl.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { StyleSheet, View } from "react-native"; const PropTypes = React.PropTypes; const F8PageControl = React.createClass({ propTypes: { style: View.propTypes.style, count: PropTypes.number.isRequired, selectedIndex: PropTypes.number.isRequired }, render: function() { const images = []; for (let i = 0; i < this.props.count; i++) { const isSelected = this.props.selectedIndex === i; images.push(); } return ( {images} ); } }); const Circle = React.createClass({ render: function() { const extraStyle = this.props.isSelected ? styles.full : styles.empty; return ; } }); const CIRCLE_SIZE = 4; const styles = StyleSheet.create({ container: { alignItems: "center", justifyContent: "center" }, innerContainer: { flexDirection: "row" }, circle: { margin: 2, width: CIRCLE_SIZE, height: CIRCLE_SIZE, borderRadius: CIRCLE_SIZE / 2 }, full: { backgroundColor: "#fff" }, empty: { backgroundColor: "#fff5" } }); module.exports = F8PageControl; module.exports.__cards__ = define => { define("Simple 2", () => ); define("Simple 5", () => ); }; ================================================ FILE: js/common/F8ScrollingHeader.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { PixelRatio, StyleSheet, Animated } from "react-native"; /* constants ================================================================ */ const TRANSLATE_Y_DISTANCE = 10; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {string} text the title text * @param {?number} trigger the scroll position to trigger intro/outro animation * @param {?number} duration intro/outro animation duration * @return {ReactElement} * ============================================================================== */ export default class F8ScrollingHeader extends React.Component { static defaultProps = { trigger: 200, duration: 180, contentInset: 30 }; constructor(props) { super(props); this.state = { revealed: false, anim: new Animated.Value(0) }; } componentWillReceiveProps(nextProps) { if (nextProps.scrollTop !== this.props.scrollTop) { let toValue = null; const { revealed } = this.state; const { trigger, scrollTop, duration } = nextProps; if (!revealed && scrollTop >= trigger) { toValue = 1; } else if (revealed && scrollTop < trigger) { toValue = 0; } if (toValue !== null) { this.setState({ revealed: toValue === 1 ? true : false }); Animated.timing(this.state.anim, { toValue, duration }).start(); } } } render() { const { text } = this.props; return ( {text} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { backgroundColor: "white", flexDirection: "row", alignItems: "center", position: "absolute", top: 0, paddingVertical: 9, paddingHorizontal: 6, borderBottomWidth: 1 / PixelRatio.get(), borderBottomColor: "rgba(153, 162, 178, 1)" }, text: { fontSize: 13, color: F8Colors.tangaroa } }); ================================================ FILE: js/common/F8SegmentedControl.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import StyleSheet from "./F8StyleSheet"; import { Text } from "./F8Text"; import F8Colors from "./F8Colors"; import F8Fonts from "./F8Fonts"; import { View, TouchableOpacity } from "react-native"; /* constants ================================================================ */ const BUTTON_HEIGHT = 32, CONTAINER_PADDING_B = 12, NOTIFICATION_ICON_SIZE = 5; /* ============================================================================= */ class F8SegmentedControl extends React.Component { props: { values: Array, selectedIndex: number, onChange: (newIndex: number) => void, style?: any }; static defaultProps = { backgroundColor: F8Colors.blue, textColor: F8Colors.white, borderColor: F8Colors.white }; render() { const { backgroundColor, borderColor, textColor } = this.props; const segments = this.props.values.map((value, index) => ( this.props.onChange(index)} /> )); return ( {segments} ); } } class Segment extends React.Component { props: { value: string, borderColor: ?string, textColor: ?string, isSelected: boolean, onPress: () => void }; render() { const { value, isSelected, borderColor, textColor } = this.props; const title = value && value.toUpperCase(); let selectedButtonStyle; if (isSelected) { selectedButtonStyle = { borderColor }; } let accessibilityTraits = ["button"]; if (isSelected) { accessibilityTraits.push("selected"); } return ( {title} {this.renderNotificationIcon()} ); } renderNotificationIcon() { if (this.props.hasUpdates) { return ; } else { return null; } } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { flexDirection: "row", paddingBottom: CONTAINER_PADDING_B, justifyContent: "center", alignItems: "center" }, button: { borderColor: "transparent", alignItems: "center", justifyContent: "center", backgroundColor: "transparent", height: BUTTON_HEIGHT, paddingHorizontal: 20, borderRadius: BUTTON_HEIGHT / 2, borderWidth: 1 }, label: { fontSize: 13, color: "white", fontFamily: F8Fonts.helvetica }, notificationIcon: { position: "absolute", right: 11, top: 7, width: NOTIFICATION_ICON_SIZE, height: NOTIFICATION_ICON_SIZE, backgroundColor: F8Colors.yellow, borderRadius: NOTIFICATION_ICON_SIZE / 2 } }); /* exports ================================================================== */ module.exports = F8SegmentedControl; ================================================ FILE: js/common/F8StyleSheet.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { StyleSheet, Platform } from "react-native"; export default { create(styles: Object): { [name: string]: number } { const platformStyles = {}; Object.keys(styles).forEach(name => { let { ios, android, ...style } = { ...styles[name] }; if (ios && Platform.OS === "ios") { style = { ...style, ...ios }; } if (android && Platform.OS === "android") { style = { ...style, ...android }; } platformStyles[name] = style; }); return StyleSheet.create(platformStyles); } }; ================================================ FILE: js/common/F8Text.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; // Dependencies // ============================================================================= import React from "react"; import ReactNative from "react-native"; import F8Colors from "./F8Colors"; import F8Fonts from "./F8Fonts"; import StyleSheet from "./F8StyleSheet"; // Text Elements // ============================================================================= export function Text({ style, ...props }: Object): ReactElement { return ; } export function Heading1({ style, ...props }: Object): ReactElement { return ; } export function Heading2({ style, ...props }: Object): ReactElement { return ; } export function Heading3({ style, ...props }: Object): ReactElement { return ; } export function Heading4({ style, ...props }: Object): ReactElement { return ; } export function Heading5({ style, ...props }: Object): ReactElement { return ; } export function Paragraph({ style, ...props }: Object): ReactElement { return ; } // export function Hyperlink({style, ...props}: Object): ReactElement { // return ; // } export function HeaderTitle({ style, ...props }: Object): ReactElement { return ; } export function HorizontalRule({ style, ...props }: Object): ReactElement { return ; } // Styles // ============================================================================= const styles = StyleSheet.create({ text: { fontFamily: F8Fonts.default }, h1: { fontFamily: F8Fonts.h1, fontSize: F8Fonts.normalize(30), lineHeight: F8Fonts.lineHeight(37), color: F8Colors.blue }, h2: { fontFamily: F8Fonts.h2, fontSize: F8Fonts.normalize(23), lineHeight: F8Fonts.lineHeight(27), //, 1.4 color: F8Colors.tangaroa, letterSpacing: -0.24 }, h3: { fontFamily: F8Fonts.h3, fontSize: F8Fonts.normalize(17), lineHeight: F8Fonts.lineHeight(20), color: F8Colors.sapphire, letterSpacing: -0.11 }, h4: { fontFamily: F8Fonts.h4, fontSize: F8Fonts.normalize(13), lineHeight: F8Fonts.lineHeight(22), color: F8Colors.tangaroa }, h5: { fontFamily: F8Fonts.helvetica, fontSize: F8Fonts.normalize(13), lineHeight: F8Fonts.lineHeight(22), color: F8Colors.tangaroa }, p: { fontFamily: F8Fonts.p, fontSize: F8Fonts.normalize(17), lineHeight: F8Fonts.lineHeight(25), //, 1.25 color: F8Colors.tangaroa }, // a: { // color: F8Colors.blue, // textDecorationLine: 'underline', // }, hr: { height: 1, backgroundColor: F8Colors.colorWithAlpha("black", 0.1) }, headerTitle: { fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold"), ios: { fontSize: 17 }, android: { fontSize: 20 } } }); ================================================ FILE: js/common/F8TimelineBackground.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ import React from "react"; import { View } from "react-native"; import LinearGradient from "react-native-linear-gradient"; import StyleSheet from "./F8StyleSheet"; import F8Colors from "./F8Colors"; import F8TimelineSegment from "./F8TimelineSegment"; /* ============================================================================= ============================================================================= */ class F8TimelineBackground extends React.Component { static defaultProps = { left: 77, height: 118, fadeOut: true }; render() { const { style, left, fadeOut, height } = this.props; let gradient = fadeOut ? this.renderGradient() : null; return ( {gradient} ); } renderGradient() { return ( ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: {}, gradient: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0 } }); /* Export ============================================================================= */ export default F8TimelineBackground; ================================================ FILE: js/common/F8TimelineSegment.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ import React from "react"; import { View, Image } from "react-native"; import StyleSheet from "./F8StyleSheet"; import F8Colors from "./F8Colors"; /* ============================================================================= -------------------------------------------------------------------------------- Description: Timeline & dot component to be used in table cells Props: ? left:number -> absolute offset from left of parent view ? color:string -> timeline color ? dotColor:string -> default dot color ? dotColorActive:string -> active dot color ? dotOffsetTop:number -> offset from (to align with text) ============================================================================= */ class F8TimelineSegment extends React.Component { static defaultProps = { line: true, dot: true, left: 24, lineOffsetTop: 0, dotOffsetTop: 0, dotSize: 7, lineWidth: 1, color: F8Colors.colorWithAlpha("tangaroa", 0.2), // TODO: Should be 0.4 alpha dotColor: F8Colors.colorWithAlpha("tangaroa", 0.2), active: false, dotColorActive: F8Colors.pink }; constructor(props) { super(props); this.styles = StyleSheet.create({ container: { position: "absolute", left: props.left - props.dotSize / 2, width: props.dotSize, top: 0, bottom: 0 }, timelineLine: { position: "absolute", left: props.dotSize / 2 - props.lineWidth / 2, top: props.lineOffsetTop, bottom: 0, width: props.lineWidth, backgroundColor: props.color }, timelineDot: { position: "absolute", width: props.dotSize, height: props.dotSize, borderRadius: props.dotSize / 2, top: props.dotOffsetTop, left: 0, borderWidth: 1, backgroundColor: F8Colors.bianca, borderColor: props.dotColor }, timelineDotActive: { backgroundColor: props.dotColorActive } }); } render() { return ( {this.renderLine()} {this.renderDot()} ); } renderLine() { if (!this.props.line) { return null; } else { return ; } } renderDot() { if (!this.props.dot) { return null; } const { active, dotIconDefault, dotIconActive, dotOffsetTop, dotSize } = this.props; if (dotIconDefault && dotIconActive) { return ( ); } else { return ( ); } } } /* Export ============================================================================= */ export default F8TimelineSegment; ================================================ FILE: js/common/F8Toast.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { Heading3, Paragraph } from "./F8Text"; import { Modal, Animated, View, StyleSheet } from "react-native"; /* constants ================================================================ */ const INTRO_DELAY_DUR = 300, SHOW_DELAY_DUR = 800, OUTRO_ANIM_DUR = 150, TRANSLATE_Y_DISTANCE = 60; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {?function} onComplete Outro animation callback * @param {?string} title At least one of title/text is required * @param {?string} text At least one of title/text is required * @param {?string} backgroundColor Container background color * @param {?string} titleColor Heading text color * @param {?string} textColor Paragraph text color * @return {ReactElement} * ============================================================================== */ class F8Toast extends React.Component { static defaultProps = { backgroundColor: F8Colors.colorWithAlpha("tangaroa", 0.95), titleColor: F8Colors.white, textColor: F8Colors.white, onComplete: _ => {} }; constructor() { super(); this.state = { contentAnimation: new Animated.Value(0) }; this.animatedContentStyles = { opacity: this.state.contentAnimation, transform: [ { translateY: this.state.contentAnimation.interpolate({ inputRange: [0, 1], outputRange: [TRANSLATE_Y_DISTANCE, 0] }) } ] }; this.intro(); } render() { const { title, text, backgroundColor, titleColor, textColor } = this.props; if (!title && !text) { return null; } return ( {title ? ( {title} ) : null} {text ? ( {text} ) : null} ); } intro = _ => { Animated.spring(this.state.contentAnimation, { delay: INTRO_DELAY_DUR, toValue: 1 }).start(this.outro); }; outro = _ => { Animated.timing(this.state.contentAnimation, { delay: SHOW_DELAY_DUR, toValue: 0, duration: OUTRO_ANIM_DUR }).start(this.props.onComplete); }; } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { flex: 1, padding: 40, alignItems: "center", justifyContent: "center" }, content: { paddingVertical: 14, paddingHorizontal: 36, borderRadius: 4 }, title: { marginBottom: 4, textAlign: "center" }, text: { textAlign: "center" } }); /* exports & Playground cards =============================================== */ module.exports = F8Toast; module.exports.__cards__ = define => { define("Default", _ => ); }; ================================================ FILE: js/common/F8Tooltip.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { HeaderTitle, Text } from "./F8Text"; import { Modal, Animated, View, StyleSheet, Image } from "react-native"; import Hitbox from "./Hitbox"; const ARROW_WIDTH = 23, ARROW_HEIGHT = 13, TOOLTIP_INSET = 23, TRANSLATE_Y_DISTANCE = 10; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {?string} title at least one of title/text is required (no content, no render) * @param {?string} text at least one of title/text is required (no content, no render) * @param {?number} x x position * @param {?number} y x position * @param {?string} align top or bottom placement of tooltip // TODO * @param {?string} hitboxColor background overlay color * @param {?function} onDismiss Callback * @return {ReactElement} * ============================================================================== */ class F8Tooltip extends React.Component { static defaultProps = { x: 0, y: 0, align: "top", hitboxColor: F8Colors.colorWithAlpha("tangaroa", 0.2), onDismiss: _ => {} }; constructor(props) { super(props); this.state = { anim: new Animated.Value(props.visible ? 1 : 0) }; this.animatedTransforms = { transform: [ { translateY: this.state.anim.interpolate({ inputRange: [0, 1], outputRange: [TRANSLATE_Y_DISTANCE, 0] }) } ] }; } componentWillReceiveProps(nextProps) { if (nextProps.visible !== this.props.visible && nextProps.visible) { this.state.anim.setValue(0); Animated.spring(this.state.anim, { toValue: 1 }).start(); } } render() { const { x, y, visible, title, text, hitboxColor, onDismiss } = this.props; if (!title && !text) { return null; } const { left, top } = this.getArrowPosition(x, y); return ( {title ? ( {title} ) : null} {text ? {text} : null} ); } getArrowPosition(x, y) { const left = x - ARROW_WIDTH / 2; const top = y; return { left, top }; } } // StyleSheet ================================================================== const styles = StyleSheet.create({ modal: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0 }, hitbox: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0 }, tooltip: { position: "absolute", top: 0, left: 0, right: 0, padding: TOOLTIP_INSET }, arrow: { position: "absolute", left: 0, top: TOOLTIP_INSET - ARROW_HEIGHT + 1 // prevent hairline separation }, content: { backgroundColor: F8Colors.white, borderRadius: 4, paddingTop: 21, paddingBottom: 16, // 5 less to account for marginBottom's paddingHorizontal: 24, alignItems: "center" }, title: { marginBottom: 5, color: F8Colors.blue, textAlign: "center" }, text: { marginBottom: 5, textAlign: "center" } }); // export ====================================================================== export default F8Tooltip; ================================================ FILE: js/common/F8Touchable.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import { TouchableHighlight, TouchableNativeFeedback, Platform } from "react-native"; function F8TouchableIOS(props: Object): ReactElement { return ( ); } const F8Touchable = Platform.OS === "android" ? TouchableNativeFeedback : F8TouchableIOS; module.exports = F8Touchable; ================================================ FILE: js/common/F8WebView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import F8Header from "./F8Header"; import F8Linking from "./F8Linking"; import StyleSheet from "./F8StyleSheet"; import { Platform, InteractionManager, View, Keyboard, PixelRatio, WebView, TouchableOpacity, Image } from "react-native"; /* constants ================================================================ */ const NAVBAR_HEIGHT_IOS = 45, STATUS_BAR_HEIGHT_ANDROID = Platform.OS === "android" && Platform.Version && Platform.Version < 21 ? 0 : 25, NAVBAR_HEIGHT_ANDROID = 55 + STATUS_BAR_HEIGHT_ANDROID, // old android status bar issue (fix from F8Header) DISABLED_OPACITY = 0.3; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {string} url WebView source prop * @param {AnyObject} navigator F8Navigator for back button 'pop' * @param {?string} backgroundColor Optional header background color * @param {?string} titleColor Optional header title color * @param {?string} itemsColor Optional header items color * @return {ReactElement} * ============================================================================== */ class F8WebView extends React.Component { static defaultProps = { backgroundColor: F8Colors.salmon, titleColor: F8Colors.white, itemsColor: F8Colors.white }; constructor(props) { super(props); const { url } = this.props; this.state = { url, canNavigateBack: false, canNavigateForward: false }; } render() { return ( {this.renderPlatformHeader()} (this._webview = c)} onNavigationStateChange={this.onNavigationStateChange} style={styles.webview} source={{ uri: this.props.url }} scalesPageToFit={true} /> {Platform.OS === "ios" ? this.renderNavigationBarIOS() : null} ); } renderPlatformHeader() { const { backgroundColor, titleColor, itemsColor } = this.props; if (Platform.OS === "ios") { return ( ); } else { return ( this.navigate("BACK")} onForward={_ => this.navigate("FORWARD")} onBrowser={_ => this.navigate("BROWSER")} /> ); } } renderNavigationBarIOS() { return ( this.navigate("BACK")} onForward={_ => this.navigate("FORWARD")} onBrowser={_ => this.navigate("BROWSER")} /> ); } navigate(intent) { if (intent === "BACK" && this.state.canNavigateBack) { this._webview.goBack && this._webview.goBack(); } else if (intent === "FORWARD" && this.state.canNavigateForward) { this._webview.goForward && this._webview.goForward(); } else if (intent === "BROWSER") { F8Linking.openURL(this.state.url); } } onNavigationStateChange = navState => { this.setState({ canNavigateBack: navState.canGoBack, canNavigateForward: navState.canGoForward, url: navState.url }); }; dismiss = _ => { this.props.navigator.pop(); Keyboard.dismiss(); }; } /* ============================================================================= (iOS & Android) -------------------------------------------------------------------------------- Wait until WebView is ready before rendering children. ============================================================================= */ class Loading extends React.Component { state = { loaded: false }; componentDidMount() { InteractionManager.runAfterInteractions(() => this.setState({ loaded: true }) ); } render() { if (this.state.loaded) { return React.Children.only(this.props.children); } return null; } } /* ============================================================================= -------------------------------------------------------------------------------- Back, forward and open in a browser ============================================================================= */ class WebViewNavigationIOS extends React.Component { render() { return ( this.props.onBack && this.props.onBack()} disabled={!this.props.canBack} style={styles.navBarAction} > this.props.onForward && this.props.onForward()} disabled={!this.props.canForward} style={styles.navBarAction} > this.props.onBrowser && this.props.onBrowser()} style={styles.navBarAction} > ); } } /* ============================================================================= -------------------------------------------------------------------------------- Back, forward and open in a browser ============================================================================= */ class WebViewNavigationAndroid extends React.Component { render() { const { backgroundColor, itemsColor } = this.props; return ( this.props.onDismiss && this.props.onDismiss()} style={styles.navBarDismiss} > this.props.onBack && this.props.onBack()} disabled={!this.props.canBack} style={styles.navBarAction} > this.props.onForward && this.props.onForward()} disabled={!this.props.canForward} style={styles.navBarAction} > this.props.onBrowser && this.props.onBrowser()} style={styles.navBarAction} > ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.bianca }, webview: { flex: 1 }, navBar: { ios: { flexDirection: "row", justifyContent: "space-between", height: NAVBAR_HEIGHT_IOS, backgroundColor: F8Colors.bianca, borderTopWidth: 1 / PixelRatio.get(), borderColor: F8Colors.magnesium }, android: { height: NAVBAR_HEIGHT_ANDROID, paddingHorizontal: 10, paddingBottom: 10, justifyContent: "flex-end" } }, navBarContent: { android: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" } }, navBarArrows: { flexDirection: "row", justifyContent: "space-between", ios: { width: 150 }, android: { width: 122, alignItems: "center" } }, navBarDismiss: { android: { padding: 5 } }, navBarAction: { ios: { justifyContent: "center", paddingHorizontal: 15 }, android: { padding: 5 } } }); /* playground cards ========================================================= */ const webView = F8WebView; webView.__cards__ = define => { define("Default", _ => ); }; /* exports ================================================================== */ module.exports = webView; ================================================ FILE: js/common/Hitbox.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { TouchableWithoutFeedback, View } from "react-native"; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {function} onPress Callback * @return {ReactElement} * ============================================================================== */ export default class Hitbox extends React.Component { render() { return ( ); } } ================================================ FILE: js/common/ItemsWithSeparator.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { PixelRatio, StyleSheet, View } from "react-native"; class ItemsWithSeparator extends React.Component { props: { style?: any, separatorStyle?: any, children?: any }; render() { const children = []; const length = React.Children.count(this.props.children); React.Children.forEach(this.props.children, (child, ii) => { children.push(child); if (ii !== length - 1) { children.push( ); } }); return {children}; } } const styles = StyleSheet.create({ separator: { backgroundColor: "#0322500A", height: 1 / PixelRatio.get() } }); module.exports = ItemsWithSeparator; ================================================ FILE: js/common/LaunchScreen.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { Dimensions, StyleSheet, View, Image } from "react-native"; const WIN_WIDTH = Dimensions.get("window").width, WIN_HEIGHT = Dimensions.get("window").height; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @return {ReactElement} * ============================================================================== */ class LaunchScreen extends React.Component { static __cards__; render() { return ( ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.bianca }, image: { position: "absolute", left: 0, top: 0, width: WIN_WIDTH, height: WIN_HEIGHT, resizeMode: "cover" } }); /* playground cards ========================================================= */ const launchScreen = LaunchScreen; launchScreen.__cards__ = define => { define("Default", _ => ); }; /* exports ================================================================== */ module.exports = launchScreen; ================================================ FILE: js/common/ListContainer.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Animated from "Animated"; import NativeModules from "NativeModules"; import F8Header from "./F8Header"; import F8SegmentedControl from "./F8SegmentedControl"; import React from "react"; import ReactNative from "react-native"; import StyleSheet from "./F8StyleSheet"; import View from "View"; import ViewPager from "./ViewPager"; import F8Colors from "./F8Colors"; import ActionsOverlay from "./ActionsOverlay"; import type { Item as HeaderItem } from "./F8Header"; type Props = { title: string, leftItem?: HeaderItem, rightItem?: HeaderItem, extraItems?: Array, selectedSegment?: number, selectedSectionColor: string, backgroundImage: number, backgroundColor: string, parallaxContent?: ?ReactElement, stickyHeader?: ?ReactElement, onSegmentChange?: (segment: number) => void, children?: any }; type State = { idx: number, anim: Animated.Value, stickyHeaderHeight: number }; class ListContainer extends React.Component { props: Props; state: State; _refs: Array; _pinned: any; static defaultProps = { selectedSectionColor: "white" }; static contextTypes = { openDrawer: React.PropTypes.func, hasUnreadNotifications: React.PropTypes.number }; constructor(props: Props) { super(props); this.state = { idx: this.props.selectedSegment || 0, stickyHeaderHeight: 0 }; (this: any).handleSelectSegment = this.handleSelectSegment.bind(this); this._refs = []; } render() { const segments = []; const content = React.Children.map(this.props.children, (child, idx) => { segments.push({ title: child.props.title, hasUpdates: child.props.hasUpdates }); return React.cloneElement(child, { ref: ref => { this._refs[idx] = ref; }, // onScroll: (e) => this.handleScroll(idx, e), style: styles.listView, showsVerticalScrollIndicator: false, scrollEventThrottle: 16, // contentInset: {bottom: 141, top: 0}, automaticallyAdjustContentInsets: false, // renderHeader: this.renderFakeHeader, scrollsToTop: idx === this.state.idx }); }); let { stickyHeader } = this.props; if (segments.length > 1) { stickyHeader = ( {stickyHeader} ); } let modalClose; if (this.props.modalClose) { modalClose = ; } return ( {this.props.headerContent} {stickyHeader} {content} {modalClose} ); } componentWillReceiveProps(nextProps: Props) { if ( typeof nextProps.selectedSegment === "number" && nextProps.selectedSegment !== this.state.idx ) { this.setState({ idx: nextProps.selectedSegment }); } } componentDidUpdate(prevProps: Props, prevState: State) { if (!NativeModules.F8Scrolling) { return; } if ( this.state.idx !== prevState.idx || this.state.stickyHeaderHeight !== prevState.stickyHeaderHeight ) { if ( this._refs[prevState.idx] && this._refs[prevState.idx].getScrollResponder ) { const oldScrollViewTag = ReactNative.findNodeHandle( this._refs[prevState.idx].getScrollResponder() ); NativeModules.F8Scrolling.unpin(oldScrollViewTag); } } } handleSelectSegment(idx: number) { if (this.state.idx !== idx) { const { onSegmentChange } = this.props; this.setState({ idx }, () => onSegmentChange && onSegmentChange(idx)); } } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.bianca }, headerWrapper: { android: { // elevation: 2, backgroundColor: "transparent", // FIXME: elevation doesn't seem to work without setting border borderRightWidth: 1, marginRight: -1, borderRightColor: "transparent" } }, listView: { flex: 1, backgroundColor: "transparent" } }); module.exports = ListContainer; ================================================ FILE: js/common/LoginButton.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import { StyleSheet, Alert } from "react-native"; import F8Button from "./F8Button"; import { logInWithFacebook } from "../actions"; import { connect } from "react-redux"; class LoginButton extends React.Component { props: { style: any, source?: string, // For Analytics dispatch: (action: any) => Promise, onLoggedIn: ?() => void }; state: { isLoading: boolean }; _isMounted: boolean; constructor() { super(); this.state = { isLoading: false }; } componentDidMount() { this._isMounted = true; } componentWillUnmount() { this._isMounted = false; } render() { if (this.state.isLoading) { return ( {}} /> ); } return ( this.logIn()} /> ); } async logIn() { const { dispatch, onLoggedIn } = this.props; this.setState({ isLoading: true }); try { await Promise.all([dispatch(logInWithFacebook(this.props.source))]); } catch (e) { const message = e.message || e; if (message !== "Timed out" && message !== "Canceled by user") { Alert.alert(message); } return; } finally { this._isMounted && this.setState({ isLoading: false }); } onLoggedIn && onLoggedIn(); } } const styles = StyleSheet.create({ button: { alignSelf: "center", width: 284 } }); module.exports = connect()(LoginButton); ================================================ FILE: js/common/MapView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; /* Dependencies ============================================================================= */ import React from "react"; import { Platform, View, ScrollView, Image, PixelRatio, InteractionManager } from "react-native"; import StyleSheet from "./F8StyleSheet"; import type { Map } from "../reducers/maps"; import PhotoView from "react-native-photo-view"; /* ============================================================================= ============================================================================= */ class MapView extends React.Component { props: { map: ?Map, style?: any, zoomable: boolean, width: number, height?: number }; static defaultProps = { zoomable: false }; render() { const { map } = this.props; const mapRatio = map && map.width && map.height ? map.width / map.height : 1; if (this.props.zoomable) { return ( ); } else { return ( ); } } } /* ============================================================================= -------------------------------------------------------------------------------- Default display of 1-3x map images ============================================================================= */ class MapViewDefault extends React.Component { _isMounted: boolean; state: { loaded: boolean }; constructor() { super(); this.state = { loaded: false }; this._isMounted = false; } componentDidMount() { this._isMounted = true; InteractionManager.runAfterInteractions(() => { this._isMounted && this.setState({ loaded: true }); }); } componentWillUnmount() { this._isMounted = false; } render() { const { map, width, height } = this.props; return this.state.loaded ? ( ) : null; } } /* ============================================================================= -------------------------------------------------------------------------------- Zoomable display of 1-3x map images ============================================================================= */ class MapViewZoomable extends React.Component { static defaultProps = { maximumZoomScale: 2 }; render() { const { map, width, height } = this.props; if (Platform.OS === "ios") { const paddingTop = 0; const paddingBottom = 80; const paddedHeight = height - paddingTop - paddingBottom; const paddedWidth = paddedHeight * (map.width / map.height); return ( ); } else { return ( ); } } } /* Get Map URL (1-3x) depending on device PixelRatio ============================================================================= */ function urlForMap(map: ?Map): string { if (!map) { return ""; } switch (PixelRatio.get()) { case 1: return map.x1url; case 2: return map.x2url; case 3: return map.x3url; } return map.x3url; } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ defaultMap: { resizeMode: "contain" } }); /* Export ============================================================================= */ module.exports = MapView; ================================================ FILE: js/common/MessengerChatHead.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { StyleSheet, View, TouchableOpacity, Image } from "react-native"; import F8Linking from "./F8Linking"; import ProfilePicture from "./ProfilePicture"; import MessengerModal from "./MessengerModal"; /* constants ================================================================ */ const LOGO_OFFSET_RIGHT = 5, LOGO_OFFSET_BOTTOM = 7, PROFILE_PICTURE_SIZE = 54, CONTAINER_WIDTH = PROFILE_PICTURE_SIZE + LOGO_OFFSET_RIGHT, CONTAINER_HEIGHT = PROFILE_PICTURE_SIZE + LOGO_OFFSET_BOTTOM; /* ============================================================================= ============================================================================= */ class MessengerChatHead extends React.Component { constructor() { super(); this.state = { messengerModal: false }; } render() { const { user } = this.props; if (!user && user.id) { return false; } else { return ( this.setState({ messengerModal: false })} /> ); } } onPress = _ => { const { user } = this.props; // If possible, try and link to friend's fb profile URL. If friend object // has no profile link, go to m.me to contact them via Messenger instead const userUrl = user.link || "https://m.me"; F8Linking.openURL(userUrl).catch(() => this.setState({ messengerModal: true }) ); }; } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { width: CONTAINER_WIDTH, height: CONTAINER_HEIGHT }, logo: { position: "absolute", right: 0, bottom: 0 } }); /* export ============================================================================= */ export default MessengerChatHead; ================================================ FILE: js/common/MessengerModal.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import F8Modal from "./F8Modal"; import F8Button from "./F8Button"; import F8Fonts from "./F8Fonts"; import { Platform, StyleSheet, View, Image, Alert } from "react-native"; import F8Linking from "./F8Linking"; import { Paragraph } from "./F8Text"; /* constants ================================================================ */ const APP_STORE = "https://itunes.apple.com/us/app/messenger/id454638411", PLAY_STORE = "https://play.google.com/store/apps/details?id=com.facebook.orca"; /* ============================================================================= ============================================================================= */ class MessengerModal extends React.Component { static __cards__; render() { return ( ); } renderContent = () => { return ( Install the Messenger app {" "} to communicate with friends. this.openAppStore()} /> ); }; renderFooter = () => { return ( this.props.dismiss && this.props.dismiss()} /> ); }; openAppStore = () => { const url = Platform.OS === "ios" ? APP_STORE : PLAY_STORE; F8Linking.canOpenURL(url) .then(supported => { if (!supported) { this.props.dismiss && this.props.dismiss(); } else { return F8Linking.openURL(url); } }) .catch(() => {}); }; } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ content: { alignItems: "center", paddingVertical: 45, paddingHorizontal: 37 }, text: { marginVertical: 20, textAlign: "center" }, boldText: { fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold") } }); /* Playground Cards ========================================================= */ const messengerModal = MessengerModal; messengerModal.__cards__ = define => { define("Default", () => ( Alert.alert("Dismissed")} /> )); }; /* exports ================================================================== */ module.exports = messengerModal; ================================================ FILE: js/common/ParallaxBackground.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Animated from "Animated"; import resolveAssetSource from "resolveAssetSource"; import React from "react"; import StyleSheet from "StyleSheet"; import Dimensions from "Dimensions"; // TODO: Remove this magic numbers const HEIGHT = Dimensions.get("window").height > 600 ? 200 : 150; const SCREEN_WIDTH = Dimensions.get("window").width; type Props = { maxHeight: number, minHeight: number, offset: Animated.Value, backgroundImage: number, backgroundShift: number, // 0..1 backgroundColor: string, // TODO: This makes it seems like image loads faster. Remove children?: any }; type State = { shift: Animated.Value }; class ParallaxBackground extends React.Component { props: Props; state: State; static HEIGHT = HEIGHT; constructor(props: Props) { super(props); this.state = { shift: new Animated.Value(props.backgroundShift || 0) }; } componentDidUpdate(prevProps: Props) { if (prevProps.backgroundShift !== this.props.backgroundShift) { Animated.timing(this.state.shift, { toValue: this.props.backgroundShift, duration: 300 }).start(); } } render(): ReactElement { const { minHeight, maxHeight, offset, backgroundColor } = this.props; const buffer = 10; // To reduce visual lag when scrolling const height = offset.interpolate({ inputRange: [0, maxHeight - minHeight], outputRange: [maxHeight + buffer, minHeight + buffer], extrapolateRight: "clamp" }); return ( {this.renderBackgroundImage()} {this.renderContent()} ); } renderBackgroundImage(): ?ReactElement { const { backgroundImage, minHeight, maxHeight, offset } = this.props; if (!backgroundImage) { return null; } const source = resolveAssetSource(backgroundImage); if (!source) { return null; } const { width } = source; const translateX = this.state.shift.interpolate({ inputRange: [0, 1], outputRange: [0, SCREEN_WIDTH - width], extrapolate: "clamp" }); const length = maxHeight - minHeight; const translateY = offset.interpolate({ inputRange: [0, length / 2, length], outputRange: [0, -length / 2, -length / 1.5], extrapolate: "clamp" }); // Sometimes image width is smaller than device's width const initialScale = Math.max(SCREEN_WIDTH / width * 2 - 1, 1); const scale = offset.interpolate({ inputRange: [-length, 0], outputRange: [2, initialScale], extrapolateRight: "clamp" }); const transforms = { transform: [{ translateX }, { translateY }, { scale }] }; return ; } renderContent(): ?ReactElement { if (React.Children.count(this.props.children) === 0) { return null; } const content = React.Children.only(this.props.children); const { minHeight, maxHeight, offset } = this.props; const length = maxHeight - minHeight; const opacity = offset.interpolate({ inputRange: [0, length - 40], outputRange: [1, 0], extrapolate: "clamp" }); const translateY = offset.interpolate({ inputRange: [0, length], outputRange: [-32, -(length / 2) - 32], extrapolate: "clamp" }); const transforms = { opacity, transform: [{ translateY }] }; return ( {content} ); } } const HEADER_HEIGHT = HEIGHT + 156; const styles = StyleSheet.create({ container: { position: "absolute", left: 0, top: 0, right: 0, overflow: "hidden" }, contentContainer: { position: "absolute", left: 0, top: 0, right: 0, height: HEADER_HEIGHT, alignItems: "center", justifyContent: "center", backgroundColor: "transparent" } }); module.exports = ParallaxBackground; ================================================ FILE: js/common/PlayButton.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "./F8Colors"; import { StyleSheet, View, TouchableOpacity, Image, Alert } from "react-native"; /* constants ================================================================ */ const BUTTON_SIZE = 76, BUTTON_SIZE_SM = 16; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {?string} type Display style default "large" || "small" * @param {?string} buttonColor Background color * @param {?string} iconColor Icon tint color * @param {?function} onPress event callback * @return {ReactElement} * ============================================================================== */ class PlayButton extends React.Component { static defaultProps = { type: "large", buttonColor: F8Colors.yellow, iconColor: F8Colors.pink }; render() { // color theming (with defaults) const buttonColorStyles = { backgroundColor: this.props.buttonColor }; const iconColorStyles = { tintColor: this.props.iconColor }; // size variation const { buttonSizeStyles, iconSizeStyles, iconImage } = this.getSizeStyles(); // icon element const image = ( ); // return TouchableOpacity container if there's an onPress handler else use a View if (this.props.onPress) { return ( {image} ); } else { return ( {image} ); } } onPress = _ => { this.props.onPress && this.props.onPress(); }; getSizeStyles() { const { type } = this.props; let buttonSizeStyles, iconSizeStyles, iconImage; if (type === "small") { buttonSizeStyles = { width: BUTTON_SIZE_SM, height: BUTTON_SIZE_SM, borderRadius: BUTTON_SIZE_SM / 2 }; iconSizeStyles = { transform: [{ translateX: 0.5 }] }; iconImage = require("./img/buttons/play-small.png"); } else { buttonSizeStyles = { width: BUTTON_SIZE, height: BUTTON_SIZE, borderRadius: BUTTON_SIZE / 2 }; iconSizeStyles = { transform: [{ translateX: 4 }] }; iconImage = require("./img/buttons/play-large.png"); } return { buttonSizeStyles, iconSizeStyles, iconImage }; } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ button: { backgroundColor: "black", alignItems: "center", justifyContent: "center" }, icon: { tintColor: "white" } }); /* playground cards ========================================================= */ const playButton = PlayButton; playButton.__cards__ = define => { define("Large Yellow/Pink (Default)", _ => ); define("Small Yellow/Pink", _ => ); define("Large Blue/Green", _ => ( Alert.alert(" pressed!")} /> )); define("Small Pink/Yellow", _ => ( Alert.alert(" pressed!")} /> )); }; /* exports ================================================================== */ module.exports = playButton; ================================================ FILE: js/common/ProfilePicture.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { Image, PixelRatio } from "react-native"; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {String} userID Facebook user ID * @param {Number} size The desired profile photo size * @return {ReactElement} * ============================================================================== */ class ProfilePicture extends React.Component { props: { userID: string, size: number }; render() { const { userID, size } = this.props; const scaledSize = size * PixelRatio.get(); const graphURL = `https://graph.facebook.com/${userID}/picture?width=${scaledSize}&height=${scaledSize}`; return ( ); } } module.exports = ProfilePicture; ================================================ FILE: js/common/PureListView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import ListView from "ListView"; import Platform from "Platform"; import React from "react"; type Rows = Array; type RowsAndSections = { [sectionID: string]: Object }; export type Data = Rows | RowsAndSections; type RenderElement = () => ?ReactElement; type Props = { data: Data, renderEmptyList?: ?RenderElement, minContentHeight: number, contentInset: { top: number, bottom: number } }; type State = { contentHeight: number, dataSource: ListView.DataSource }; // FIXME: Android has a bug when scrolling ListView the view insertions // will make it go reverse. Temporary fix - pre-render more rows const LIST_VIEW_PAGE_SIZE = Platform.OS === "android" ? 20 : 10; class PureListView extends React.Component { props: Props; state: State; static defaultProps = { data: [], contentInset: { top: 0, bottom: 0 }, // TODO: This has to be scrollview height + fake header minContentHeight: 0 // renderSeparator: (sectionID, rowID) => , }; constructor(props: Props) { super(props); let dataSource = new ListView.DataSource({ getRowData: (dataBlob, sid, rid) => dataBlob[sid][rid], getSectionHeaderData: (dataBlob, sid) => dataBlob[sid], rowHasChanged: (row1, row2) => row1 !== row2, sectionHeaderHasChanged: (s1, s2) => s1 !== s2 }); this.state = { contentHeight: 0, containerHeight: 0, dataSource: cloneWithData(dataSource, props.data) }; (this: any).renderFooter = this.renderFooter.bind(this); (this: any).renderHeader = this.renderHeader.bind(this); (this: any).onContentSizeChange = this.onContentSizeChange.bind(this); } componentWillReceiveProps(nextProps: Props) { if (this.props.data !== nextProps.data) { this.setState({ dataSource: cloneWithData(this.state.dataSource, nextProps.data) }); } } render() { const { contentInset } = this.props; const bottom = contentInset.bottom + Math.max(0, this.props.minContentHeight - this.state.contentHeight); return ( (this._listview = c)} dataSource={this.state.dataSource} renderHeader={this.renderHeader} renderFooter={this.renderFooter} contentInset={{ bottom, top: contentInset.top }} onContentSizeChange={this.onContentSizeChange} onLayout={event => { const { height } = event.nativeEvent.layout; if (this.state.containerHeight !== height) { this.setState({ containerHeight: height }); } }} /> ); } onContentSizeChange(contentWidth: number, contentHeight: number) { if (contentHeight !== this.state.contentHeight) { this.setState({ contentHeight }); } } scrollTo(...args: Array) { this._listview.scrollTo(...args); } getScrollResponder(): any { return this._listview.getScrollResponder(); } renderHeader(): ?ReactElement { if (this.state.dataSource.getRowCount() !== 0) { return this.props.renderHeader && this.props.renderHeader(); } } renderFooter(): ?ReactElement { if (this.state.dataSource.getRowCount() === 0) { return ( this.props.renderEmptyList && this.props.renderEmptyList(this.state.containerHeight) ); } return this.props.renderFooter && this.props.renderFooter(); } } function cloneWithData(dataSource: ListView.DataSource, data: ?Data) { if (!data) { return dataSource.cloneWithRows([]); } if (Array.isArray(data)) { return dataSource.cloneWithRows(data); } return dataSource.cloneWithRowsAndSections(data); } module.exports = PureListView; ================================================ FILE: js/common/ViewPager.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, StyleSheet, ScrollView, ViewPagerAndroid, Platform } from "react-native"; type Props = { count: number, selectedIndex: number, onSelectedIndexChange?: (index: number) => void, bounces?: boolean, children?: any, style?: any }; type State = { width: number, height: number, selectedIndex: number, initialSelectedIndex: number, scrollingTo: ?number }; class ViewPager extends React.Component { props: Props; state: State; constructor(props: Props) { super(props); this.state = { width: 0, height: 0, selectedIndex: this.props.selectedIndex, initialSelectedIndex: this.props.selectedIndex, scrollingTo: null }; (this: any).handleHorizontalScroll = this.handleHorizontalScroll.bind(this); (this: any).adjustCardSize = this.adjustCardSize.bind(this); } render() { if (Platform.OS === "ios") { return this.renderIOS(); } else { return this.renderAndroid(); } } renderIOS() { return ( (this._scrollview = c)} contentOffset={{ x: this.state.width * this.state.initialSelectedIndex, y: 0 }} style={[styles.scrollview, this.props.style]} horizontal={true} pagingEnabled={true} bounces={!!this.props.bounces} scrollsToTop={false} onScroll={this.handleHorizontalScroll} scrollEventThrottle={100} removeClippedSubviews={false} automaticallyAdjustContentInsets={false} directionalLockEnabled={true} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} onLayout={this.adjustCardSize} > {this.renderContent()} ); } renderAndroid() { return ( (this._scrollview = c)} initialPage={this.state.initialSelectedIndex} onPageSelected={this.handleHorizontalScroll} style={styles.container} > {this.renderContent()} ); } adjustCardSize(e: any) { this.setState({ width: e.nativeEvent.layout.width, height: e.nativeEvent.layout.height }); } componentWillReceiveProps(nextProps: Props) { if (nextProps.selectedIndex !== this.state.selectedIndex) { if (Platform.OS === "ios") { this._scrollview.scrollTo({ x: nextProps.selectedIndex * this.state.width, animated: true }); this.setState({ scrollingTo: nextProps.selectedIndex }); } else { this._scrollview.setPage(nextProps.selectedIndex); this.setState({ selectedIndex: nextProps.selectedIndex }); } } } renderContent(): Array { const { width, height } = this.state; const style = Platform.OS === "ios" && styles.card; return React.Children.map(this.props.children, (child, i) => ( {child} )); } handleHorizontalScroll(e: any) { let selectedIndex = e.nativeEvent.position; if (selectedIndex === undefined) { selectedIndex = Math.round( e.nativeEvent.contentOffset.x / this.state.width ); } if (selectedIndex < 0 || selectedIndex >= this.props.count) { return; } if ( this.state.scrollingTo !== null && this.state.scrollingTo !== selectedIndex ) { return; } if ( this.props.selectedIndex !== selectedIndex || this.state.scrollingTo !== null ) { this.setState({ selectedIndex, scrollingTo: null }, () => { // the onSelectedIndexChange handler can change props.selectedIndex, so we want // to call it after the state has actually changed to avoid extra scrollTo call // (see componentWillReceiveProps) const { onSelectedIndexChange } = this.props; onSelectedIndexChange && onSelectedIndexChange(selectedIndex); }); } } } const styles = StyleSheet.create({ container: { flex: 1 }, scrollview: { flex: 1, backgroundColor: "transparent" }, card: { backgroundColor: "transparent" } }); module.exports = ViewPager; ================================================ FILE: js/common/__tests__/convertTimes-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; jest.autoMockOff(); import { parseTimeToUTC, sessionsHappeningNow, sessionsHappeningToday } from "../convertTimes"; // This corrects for times in Parse being inputted as local pacific time // but the timestamp being provided as a unix time epoch describe("parseTimeToUTC", () => { const pacificDiffMs = 1000 * 60 * 60 * 7; const random = 1491352767750; const day1StartUTC = 1492473600000; const day2StartUTC = 1492560000000; it("corrects Parse time from UTC to local pacific time", () => { expect(parseTimeToUTC(random)).toEqual(random + pacificDiffMs); expect(parseTimeToUTC(day1StartUTC)).toEqual(day1StartUTC + pacificDiffMs); expect(parseTimeToUTC(day2StartUTC)).toEqual(day2StartUTC + pacificDiffMs); }); }); // Whether the provided timestamp is within the range of session start and // end times describe("sessionsHappeningNow", () => { const random = 1491352767750; const mockSessionsBasic = [ { startTime: 1491352767600, endTime: 1491352767601 }, { startTime: 1491352767800, endTime: 1491352767801 } ]; const mockSessionsReversed = [ { startTime: 1491352767800, endTime: 1491352767801 }, { startTime: 1491352767600, endTime: 1491352767601 } ]; const mockSessionsWrong = [ { startTime: 1491252767800, endTime: 1491252767801 }, { startTime: 1491252767600, endTime: 1491252767601 } ]; it("returns false for empty sessions", () => { expect(sessionsHappeningNow(random, [])).toEqual(false); }); it("returns true if time is within range", () => { expect(sessionsHappeningNow(random, mockSessionsBasic)).toEqual(true); expect(sessionsHappeningNow(random, mockSessionsReversed)).toEqual(true); }); it("returns false if time is outside of range", () => { expect(sessionsHappeningNow(random, mockSessionsWrong)).toEqual(false); }); }); // For a given timestamp return a boolean if that time is within either day 1 // or day 2 of the conference (for time-based functionality like hide completed) describe("sessionsHappeningToday", () => { const day0Start = "Mon Apr 17 2017 00:00:00 GMT-0700 (PDT)", // 1492412400000 day0End = "Mon Apr 17 2017 23:59:59 GMT-0700 (PDT)", // 1492498799000 day1Start = "Tue Apr 18 2017 00:00:00 GMT-0700 (PDT)", // 1492498800000 day1Middle = "Tue Apr 18 2017 12:00:00 GMT-0700 (PDT)", // 1492542000000 day1End = "Tue Apr 18 2017 23:59:59 GMT-0700 (PDT)", // 1492585199000 day2Start = "Wed Apr 19 2017 00:00:00 GMT-0700 (PDT)", // 1492585200000 day2Middle = "Wed Apr 19 2017 12:00:00 GMT-0700 (PDT)", // 1492628400000 day2End = "Wed Apr 19 2017 23:59:59 GMT-0700 (PDT)", // 1492671599000 day3Start = "Thu Apr 20 2017 00:00:00 GMT-0700 (PDT)", // 1492671600000 day3End = "Thu Apr 20 2017 23:59:59 GMT-0700 (PDT)"; // 1492757999000 it("returns false before the conference", () => { // the day before expect(sessionsHappeningToday(new Date(day0Start).getTime())).toEqual( false ); expect(sessionsHappeningToday(new Date(day0End).getTime())).toEqual(false); }); it("returns true during the conference", () => { // during (day 1) expect(sessionsHappeningToday(new Date(day1Start).getTime())).toEqual(true); expect(sessionsHappeningToday(new Date(day1Middle).getTime())).toEqual( true ); expect(sessionsHappeningToday(new Date(day1End).getTime())).toEqual(true); // during (day 2) expect(sessionsHappeningToday(new Date(day2Start).getTime())).toEqual(true); expect(sessionsHappeningToday(new Date(day2Middle).getTime())).toEqual( true ); expect(sessionsHappeningToday(new Date(day2End).getTime())).toEqual(true); }); it("returns false after the conference", () => { // the day after expect(sessionsHappeningToday(new Date(day3Start).getTime())).toEqual( false ); expect(sessionsHappeningToday(new Date(day3End).getTime())).toEqual(false); }); }); ================================================ FILE: js/common/convertTimes.js ================================================ import moment from "moment-timezone"; import { timezone } from "../env.js"; import type { Session } from "../reducers/sessions"; const CONFERENCE_DATES = [1492473600000, 1492560000000]; export function parseTimeToUTC(unix: number): number { const offset = moment.tz.zone(timezone).offset(unix); const utc = moment.utc(unix); const fixed = utc.clone().add(offset, "minutes"); return fixed.valueOf(); } function minutesSinceMidnight(): number { const now = moment().tz(timezone); const dayStart = now.clone().startOf("day"); return now.diff(dayStart, "minutes"); } export function currentTimeOnConferenceDay(day: number = 1): number { const utc = moment.utc(parseTimeToUTC(CONFERENCE_DATES[day - 1])); const mappedTime = utc.clone().add(minutesSinceMidnight(), "minutes"); return mappedTime.valueOf(); } export function sessionsHappeningNow( now: number, sessions: Array = [] ): boolean { if (sessions.length) { let start, end; sessions.map(session => { if (!start || start > session.startTime) { start = session.startTime; } if (!end || end < session.endTime) { end = session.startTime; } }); return now >= start && now <= end; } else { return false; } } export function sessionsHappeningToday(now: number): boolean { const day1Start = new Date( "Tue Apr 18 2017 00:00:00 GMT-0700 (PDT)" ).getTime(); const day2End = new Date("Wed Apr 19 2017 23:59:59 GMT-0700 (PDT)").getTime(); return now >= day1Start && now <= day2End; } ================================================ FILE: js/env.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; module.exports = { version: 410, testMenuEnabled: false, parseAppID: "oss-f8-app-2017", serverURL: "http://localhost:1337", graphqlURL: "http://localhost:4000/graphql", compatibleStoreVersion: "0.10", gcmSenderId: "336769939688", timezone: "America/Los_Angeles", dayLabel(num) { const days = { 1: "Tue 4/18", 2: "Wed 4/19" }; return days[num] || `Day ${num}`; } }; ================================================ FILE: js/filter/FilterScreen.android.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import TopicItem from "./TopicItem"; import ItemsWithSeparator from "../common/ItemsWithSeparator"; import { Animated, View, ScrollView, Modal, TouchableOpacity } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import Hitbox from "../common/Hitbox"; import { HeaderTitle, Heading4 } from "../common/F8Text"; import { connect } from "react-redux"; const DRAWER_WIDTH = 300; class FilterScreen extends React.Component { props: { isLoggedIn: boolean, topics: Array, selectedTopics: { [id: string]: boolean }, dispatch: (action: any) => void, navigator: any, onClose: ?() => void }; state: { selectedTopics: { [id: string]: boolean }, anim: Animated.Value }; constructor(props) { super(props); this.state = { selectedTopics: { ...this.props.selectedTopics }, anim: new Animated.Value(0) }; (this: any).clearFilter = this.clearFilter.bind(this); (this: any).close = this.close.bind(this); } componentWillReceiveProps(nextProps) { if (this.props.selectedTopics !== nextProps.selectedTopics) { this.setState({ selectedTopics: { ...nextProps.selectedTopics } }); } } componentWillUpdate(nextProps, nextState) { if (this.props.visble !== nextProps.visible && nextProps.visible) { setTimeout(_ => { this.showDrawer(true); }, 250); } } render() { return ( {this.renderHeader()} {this.renderTopics()} ); } renderHeader() { let clearButton; if (this.hasSelectedTopics()) { clearButton = ( CLEAR ); } return ( Filter {clearButton} ); } renderTopics() { const topics = this.props.topics.map((topic, idx) => { return ( ); }); return ( {topics} ); } toggleTopic(topic) { const selectedTopics = { ...this.state.selectedTopics }; let value = !selectedTopics[topic]; if (value) { selectedTopics[topic] = true; } else { delete selectedTopics[topic]; } this.applyFilter(selectedTopics); } applyFilter(selectedTopics) { this.setState({ selectedTopics }); this.props.onApply && this.props.onApply(selectedTopics); } close() { this.showDrawer(false); setTimeout(_ => { this.props.onClose && this.props.onClose(); }, 250); } clearFilter() { this.applyFilter({}); } hasSelectedTopics() { return this.props.topics.some(topic => this.state.selectedTopics[topic]); } showDrawer(visible) { const toValue = visible ? 0 : 1; const duration = visible ? 250 : 250; Animated.timing(this.state.anim, { toValue, duration }).start(); } } const styles = StyleSheet.create({ contentWrapper: { flex: 1, alignItems: "flex-end" }, header: { height: 65, alignItems: "center", flexDirection: "row", paddingHorizontal: 23 }, contentDrawer: { flex: 1, width: DRAWER_WIDTH, backgroundColor: F8Colors.tangaroa }, scrollview: {}, separator: { backgroundColor: "rgba(20, 38, 74, 1)" } }); module.exports = connect()(FilterScreen); ================================================ FILE: js/filter/FilterScreen.ios.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Header from "../common/F8Header"; import F8Colors from "../common/F8Colors"; import TopicItem from "./TopicItem"; import F8Button from "../common/F8Button"; import ItemsWithSeparator from "../common/ItemsWithSeparator"; import { View, ScrollView } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import ActionsOverlay from "../common/ActionsOverlay"; import shallowEqual from "fbjs/lib/shallowEqual"; import { connect } from "react-redux"; class FilterScreen extends React.Component { props: { topics: Array, selectedTopics: { [id: string]: boolean }, dispatch: (action: any) => void, navigator: any, onClose: ?() => void }; state: { selectedTopics: { [id: string]: boolean } }; static defaultProps = { topics: [], selectedTopics: {} }; constructor(props) { super(props); this.state = { selectedTopics: { ...this.props.selectedTopics } }; (this: any).applyFilter = this.applyFilter.bind(this); (this: any).clearFilter = this.clearFilter.bind(this); (this: any).close = this.close.bind(this); } componentWillReceiveProps(nextProps) { if (this.props.selectedTopics !== nextProps.selectedTopics) { this.setState({ selectedTopics: { ...nextProps.selectedTopics } }); } } componentWillUpdate(nextProps, nextState) { if (this.state.selectedTopics !== nextState.selectedTopics) { const applyButtonEnabled = !shallowEqual( nextProps.selectedTopics, nextState.selectedTopics ); this.setState({ applyButtonEnabled }); } } render() { let rightItem; if (this.hasSelectedTopics()) { rightItem = { title: "Clear", icon: require("../common/img/x-white.png"), onPress: this.clearFilter }; } return ( {this.renderTopics()} {this.renderActions()} ); } renderTopics() { const topics = this.props.topics.map((topic, idx) => { return ( ); }); return ( {topics} ); } renderActions() { let applyButton; if (this.state.applyButtonEnabled) { applyButton = ( ); } else { applyButton = ( ); } return ( {applyButton} ); } toggleTopic(topic) { const selectedTopics = { ...this.state.selectedTopics }; const value = !selectedTopics[topic]; if (value) { selectedTopics[topic] = true; } else { delete selectedTopics[topic]; } this.setState({ selectedTopics }); } applyFilter() { this.props.onApply && this.props.onApply(this.state.selectedTopics); this.close(); } close() { const { navigator, onClose } = this.props; if (navigator) { requestAnimationFrame(() => navigator.pop()); } if (onClose) { onClose(); } } clearFilter() { this.setState({ selectedTopics: {} }); } hasSelectedTopics() { return this.props.topics.some(topic => this.state.selectedTopics[topic]); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.tangaroa }, scrollview: { ios: { paddingTop: 20, paddingBottom: ActionsOverlay.height } }, separator: { backgroundColor: "rgba(20, 38, 74, 1)" }, actions: { position: "absolute", left: 0, right: 0, bottom: 0 // alignItems: 'stretch' } }); module.exports = connect()(FilterScreen); ================================================ FILE: js/filter/FriendsList.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react-native"; const { Image, StyleSheet, Text, TouchableOpacity, View } = React; type Friend = { id: string, name: string }; class FriendsList extends React.Component { props: { friends: Array, onPress: (friend: Friend) => void }; render() { if (this.props.friends.length === 0) { return ( No friends have shared their schedule. ); } return ( {this.props.friends.map(friend => ( this.props.onPress(friend)} /> ))} ); } } class UserPog extends React.Component { props: { user: Friend, onPress: () => void }; render() { const { id, name } = this.props.user; const firstName = name.split(" ")[0]; // TODO: problems with i18n return ( {firstName} ); } } const SIZE = 50; const styles = StyleSheet.create({ container: { flexDirection: "row", alignItems: "center", justifyContent: "center" }, noFriends: { height: SIZE, borderRadius: SIZE / 2, backgroundColor: "rgba(3, 34, 80, 0.15)" }, pog: { alignItems: "center", margin: 6 }, profilePic: { marginBottom: 6, width: SIZE, height: SIZE, borderRadius: SIZE / 2 }, text: { color: "white", fontSize: 12, textAlign: "center" } }); module.exports = FriendsList; ================================================ FILE: js/filter/Header.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import StyleSheet from "StyleSheet"; import { Text } from "../F8Text"; import TouchableOpacity from "TouchableOpacity"; import View from "View"; class Header extends React.Component { render() { return ( {this.renderItem( this.props.leftItemTitle, this.props.onLeftItemPress )} {this.props.title} {this.renderItem( this.props.rightItemTitle, this.props.onRightItemPress )} ); } renderItem(title: string, onPress: () => void) { if (!title) { return null; } return ( {title.toUpperCase()} ); } } const STATUS_BAR_HEIGHT = 20; const HEADER_HEIGHT = STATUS_BAR_HEIGHT + /* toolbar */ 44; const styles = StyleSheet.create({ header: { backgroundColor: "transparent", paddingTop: STATUS_BAR_HEIGHT, height: HEADER_HEIGHT, flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, titleText: { color: "white", fontSize: 17 }, leftItem: { flex: 1, alignItems: "flex-start" }, centerItem: { flex: 2, alignItems: "center" }, rightItem: { flex: 1, alignItems: "flex-end" }, itemWrapper: { padding: 11 }, itemTitle: { letterSpacing: 1, fontSize: 12, color: "white" } }); module.exports = Header; ================================================ FILE: js/filter/Section.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { StyleSheet, View } from "react-native"; import { Text } from "../common/F8Text"; class Section extends React.Component { render() { const { children, title } = this.props; if (React.Children.count(children) === 0) { return null; } return ( {title.toUpperCase()} {children} ); } } const styles = StyleSheet.create({ container: { marginBottom: 50 }, title: { fontSize: 12, letterSpacing: 1, color: "#A0B7FF", textAlign: "center", margin: 10 } }); module.exports = Section; ================================================ FILE: js/filter/TopicItem.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { StyleSheet, TouchableOpacity, Image } from "react-native"; import { Text } from "../common/F8Text"; import F8Fonts from "../common/F8Fonts"; import * as TopicIcons from "./topicIcons"; /** * ============================================================================== * Filter screen list items * ------------------------------------------------------------------------------ * @extends React.Component * @param {string} topic The name of the topic * @param {number} icon the year to match against * @param {boolean} isChecked whether to show the default/active appearance * @param {function} onToggle handler for tap event * ============================================================================== */ export default class TopicItem extends React.Component { props: { topic: string, icon: number, isChecked: boolean, onToggle: (value: boolean) => void }; render() { const { topic, icon, isChecked, onToggle } = this.props; const activeIcon = TopicIcons.get("active", icon); const defaultIcon = TopicIcons.get("default", icon); const accessibilityTraits = ["button"]; if (isChecked) { accessibilityTraits.push("selected"); } return ( {topic} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { paddingVertical: 17, paddingHorizontal: 13, flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, title: { fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "offWhite"), fontSize: 17, color: "white", flex: 1 }, icon: { marginRight: 18 } }); ================================================ FILE: js/filter/topicIcons.js ================================================ const TOPIC_ICONS = { default: [require("./img/default/1.png")], active: [ require("./img/active/1.png"), require("./img/active/2.png"), require("./img/active/3.png"), require("./img/active/4.png"), require("./img/active/5.png"), require("./img/active/6.png"), require("./img/active/7.png"), require("./img/active/8.png"), require("./img/active/9.png"), require("./img/active/10.png") ] }; /** * ============================================================================== * Get filter list item icons by index and active/default state * ------------------------------------------------------------------------------ * @param {?string} state Whether to return default or active state icons * @param {?number} idx Index for varied icons * @return {number} RN Asset Source format * ============================================================================== */ export function get(state = "default", idx = 0) { // sanitize input if (!TOPIC_ICONS[state]) { return TOPIC_ICONS.default[0]; } // map provided index to the number of available options const mappedIndex = (idx + 1) % TOPIC_ICONS[state].length; return TOPIC_ICONS[state][mappedIndex]; } ================================================ FILE: js/flow-lib.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; /* eslint no-unused-vars:0 */ declare var jest: any; declare var jasmine: any; declare var describe: (name: string, callback: () => void) => void; declare var it: (name: string, callback: () => void) => void; declare var expect: any; declare module "graphql" { declare var exports: any; } declare module "react-native-linear-gradient" { declare var exports: any; } declare module "resolveAssetSource" { declare var exports: any; } ================================================ FILE: js/login/LoginModal.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import { Dimensions, View, Image, StyleSheet } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import { Heading2 } from "../common/F8Text"; import F8BackgroundRepeat from "../common/F8BackgroundRepeat"; import F8Button from "../common/F8Button"; import LoginButton from "../common/LoginButton"; import F8Modal from "../common/F8Modal"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, WINDOW_HEIGHT = Dimensions.get("window").height, RENDER_ARROW_SECTION = WINDOW_HEIGHT <= 600 ? false : true, CONTENT_PADDING_V = WINDOW_HEIGHT <= 600 ? 20 : 32, MODAL_PADDING_H = 10, MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2; /* ============================================================================= */ class LoginModal extends React.Component { props: { navigator: Navigator, onLogin: () => void }; render() { return ( ); } renderContent = _ => { return ( {"Log in to add sessions\nto My F8."} {this.renderArrow()} ); }; renderArrow() { if (RENDER_ARROW_SECTION) { return ; } else { return null; } } renderFooter = _ => { return ( ); }; dismiss = _ => { this.props.onClose && this.props.onClose(); }; loggedIn = _ => { this.props.onLogin(); this.dismiss(); }; } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ header: { alignItems: "center", overflow: "hidden" }, headerBackground: { position: "absolute", left: 0, top: 0 }, content: { paddingHorizontal: 23, paddingVertical: CONTENT_PADDING_V, alignItems: "center" }, h2: { color: F8Colors.blue, textAlign: "center", marginBottom: 20 }, arrow: { marginBottom: 20 } }); /* exports ================================================================== */ module.exports = LoginModal; ================================================ FILE: js/login/LoginScreen.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { connect } from "react-redux"; import { skipLogin } from "../actions"; import F8Colors from "../common/F8Colors"; import F8Fonts from "../common/F8Fonts"; import { Text, Heading1 } from "../common/F8Text"; import { Animated, Dimensions, Image, StatusBar, View, TouchableOpacity, StyleSheet } from "react-native"; import LoginButton from "../common/LoginButton"; /* Config/Constants ============================================================================= */ const SKIP_BTN_HEIGHT = 24, WINDOW_WIDTH = Dimensions.get("window").width, WINDOW_HEIGHT = Dimensions.get("window").height, VERTICAL_BREAKPOINT = WINDOW_HEIGHT <= 600, HEADER_HEIGHT = VERTICAL_BREAKPOINT ? 220 : 285, SKIP_BTN_MARGIN_TOP = VERTICAL_BREAKPOINT ? 15 : 23, WHENWHERE_PADDING_TOP = VERTICAL_BREAKPOINT ? 12 : 18, RENDER_ARROW_SECTION = VERTICAL_BREAKPOINT ? false : true, LOGIN_PADDING_BOTTOM = VERTICAL_BREAKPOINT ? 20 : 33, CONTENT_PADDING_H = VERTICAL_BREAKPOINT ? 15 : 20; /* ============================================================================= -------------------------------------------------------------------------------- Props: ? ============================================================================= */ class LoginScreen extends React.Component { state = { anim: new Animated.Value(0) }; componentDidMount() { Animated.timing(this.state.anim, { toValue: 3000, duration: 3000 }).start(); } render() { return ( Facebook Developer Conference APRIL 18 + 19 / SAN JOSE, CALIFORNIA {this.renderArrowSection()} Use Facebook to find your friends at F8. this.props.dispatch(skipLogin())} style={styles.skipButton} > SKIP FOR NOW ); } renderArrowSection() { if (RENDER_ARROW_SECTION) { return ( ); } else { return null; } } fadeIn(delay, from = 0) { const { anim } = this.state; return { opacity: anim.interpolate({ inputRange: [delay, Math.min(delay + 500, 3000)], outputRange: [0, 1], extrapolate: "clamp" }), transform: [ { translateY: anim.interpolate({ inputRange: [delay, Math.min(delay + 500, 3000)], outputRange: [from, 0], extrapolate: "clamp" }) } ] }; } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.bianca }, //header styles header: { height: HEADER_HEIGHT, alignItems: "center", justifyContent: "flex-end" }, headerPattern: { position: "absolute", left: 0, top: 0, right: 0, height: HEADER_HEIGHT - 30 }, headerIllustration: { position: "absolute", left: 0, width: WINDOW_WIDTH, bottom: 80 }, content: { flex: 1, justifyContent: "space-around", paddingHorizontal: CONTENT_PADDING_H }, h1: { marginTop: 16, textAlign: "center" }, whenWhereText: { marginTop: WHENWHERE_PADDING_TOP, textAlign: "center", color: F8Colors.tangaroa, fontFamily: F8Fonts.helvetica }, arrowSection: { alignItems: "center", justifyContent: "center" }, loginSection: { paddingBottom: LOGIN_PADDING_BOTTOM, alignItems: "center", paddingHorizontal: 20 }, loginComment: { textAlign: "center", fontSize: 15, color: F8Colors.pink, fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold"), marginBottom: 23 }, skipButton: { marginTop: SKIP_BTN_MARGIN_TOP, height: SKIP_BTN_HEIGHT, alignSelf: "stretch", alignItems: "center", justifyContent: "center" }, skipText: { color: F8Colors.colorWithAlpha("tangaroa", 0.5), fontFamily: F8Fonts.helvetica } }); /* Export ============================================================================= */ module.exports = connect()(LoginScreen); ================================================ FILE: js/rating/Header.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import { Image, Text, View, StyleSheet } from "react-native"; import type { Session } from "../reducers/sessions"; type Props = { session: Session }; function Header({ session }: Props) { const pics = session.speakers.map(speaker => ( )); return ( {session.title} {pics} ); } const styles = StyleSheet.create({ container: { alignItems: "center", justifyContent: "center", height: 170, paddingHorizontal: 10 }, background: { alignItems: "center", position: "absolute", bottom: 0, left: 0, right: 0 }, label: { fontSize: 12, color: F8Colors.lightText, letterSpacing: 1 }, title: { marginTop: 10, fontSize: 17, fontWeight: "bold", color: F8Colors.darkText, textAlign: "center" }, speakers: { marginTop: 15, flexDirection: "row" }, pic: { width: 40, height: 40, borderRadius: 20, margin: 2 } }); module.exports = Header; module.exports.__cards__ = define => { const MOCK_SESSION = { id: "mock1", title: "Building For the Next Billion", speakers: [ { id: "1", bio: "", name: "Foo", title: "", pic: "https://graph.facebook.com/100001244322535/picture?width=60&height=60" }, { id: "2", bio: "", name: "Bar", title: "", pic: "https://graph.facebook.com/10152531777042364/picture?width=60&height=60" } ], day: 1, allDay: false, description: "...", startTime: 0, endTime: 0, hasDetails: true, location: "space", map: "space", onMySchedule: false, slug: "next-billion", tags: [] }; define("Example", (state = null, update) => (
)); define("Long title", () => (
)); }; ================================================ FILE: js/rating/RadioButtons.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import F8Fonts from "../common/F8Fonts"; import { View, TouchableOpacity, Image } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import { Text } from "../common/F8Text"; /* ========================================================= */ class RadioButtons extends React.Component { static defaultProps = { options: [] }; render() { return ( {this.props.options.map((text, idx) => this.renderAnswer(text, idx))} ); } onPress = (value, idx) => { this.props.onChange && this.props.onChange(idx); }; renderAnswer(text, idx) { const isActive = this.props.selectedIndex === idx; const source = isActive ? require("./img/radio-active.png") : require("./img/radio-default.png"); const accessibilityTraits = ["button"]; if (isActive) { accessibilityTraits.push("selected"); } return ( this.onPress(text, idx)} > {text} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { paddingVertical: 10, flexDirection: "row", alignItems: "center" }, text: { paddingLeft: 16, fontSize: F8Fonts.normalize(17), color: F8Colors.tangaroa, flex: 1 } }); /* exports & Playground cards =============================================== */ module.exports = RadioButtons; module.exports.__cards = define => { const MOCK_RADIO_BUTTONS = ["First option", "Second option", "Third option"]; define("Default", (state = null, update) => ( )); define("Selected: First", (state = 0, update) => ( )); }; ================================================ FILE: js/rating/RatingQuestion.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import F8Fonts from "../common/F8Fonts"; import { View } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import { Paragraph } from "../common/F8Text"; import StarRating from "./StarRating"; import RadioButtons from "./RadioButtons"; import WrittenResponse from "./WrittenResponse"; import StarRatings from "./StarRatings"; /* ============================================================================= */ export default class RatingQuestion extends React.Component { render() { const type = this.getQuestionType(); return ( {this.renderQuestionText(type)} {this.renderQuestionByType(type)} ); } getQuestionType() { const { question } = this.props; if (question.written) { return "written"; } else if (question.feedback) { return "feedback"; } else if (question.radios) { return "radios"; } else if (question.ratings) { return "ratings"; } else if (question.rating) { return "rating"; } else { return null; } } renderQuestionByType(type) { const { question, rating, onChange } = this.props; if (type === "written") { const { placeholder, maxLength } = question.written; return ( ); } else if (type === "feedback") { return ( ); } else if (type === "radios") { return ( ); } else if (type === "rating") { return ( ); } else if (type === "ratings") { const { rows, labels } = question.ratings; return ( ); } else { return null; } } renderQuestionText(type) { if (type === "feedback") { return null; } else { return ( {this.props.question.text} ); } } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ questionText: { fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold"), color: F8Colors.tangaroa, fontSize: F8Fonts.normalize(17), textAlign: "center", marginBottom: 10 } }); ================================================ FILE: js/rating/RatingScreen.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, ToastAndroid, Platform } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import F8Header from "../common/F8Header"; import { connect } from "react-redux"; import { submitSurveyAnswers } from "../actions"; import type { Survey } from "../reducers/surveys"; import type { Session } from "../reducers/sessions"; import type { Dispatch } from "../actions/types"; import RatingQuestion from "./RatingQuestion"; import { Heading2, Paragraph } from "../common/F8Text"; import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; import F8Colors from "../common/F8Colors"; import F8Button from "../common/F8Button"; import F8Toast from "../common/F8Toast"; type Props = { sessions: Array, surveys: Array, navigator: any, dispatch: Dispatch }; class RatingScreen extends React.Component { props: Props; state: { selectedIndex: number }; static defaultProps = { type: "Session", successMessage: "Rating sent!" }; constructor(props: Props) { super(props); let defaultValues = {}; (props.survey.questions || []).map((q, idx) => { if (q.optional) { defaultValues[idx] = ""; } }); this.state = { ...defaultValues }; } render() { let rightItem; if (Platform.OS === "ios" && this.isValid()) { rightItem = { title: "Submit", icon: require("../common/img/header/confirm.png"), onPress: this.submit }; } return ( this.props.navigator.pop() }} rightItem={rightItem} /> {this.renderForm()} ); } renderForm() { const { sessions, survey, successMessage } = this.props; const session = sessions.find(s => s.id === survey.sessionId); const questions = survey.questions.map((question, ii) => ( this.setState({ [ii]: rating })} /> )); return ( {this.renderHeader(session.title, survey.description)} {questions} {Platform.OS === "android" ? this.renderSubmitButton() : null} {this.state.iosSuccessMessage ? ( ) : null} ); } renderHeader(title, description) { return ( {title ? {title} : null} {description ? ( {description} ) : null} ); } renderSubmitButton() { let btn; if (this.isValid()) { btn = ; } else { btn = ; } return {btn}; } submit = () => { const { survey } = this.props; const answers = survey.questions.map((_, ii) => this.state[ii]); this.props.dispatch(submitSurveyAnswers(survey.id, answers)).then(() => { if (Platform.OS === "ios") { this.setState({ iosSuccessMessage: true }); } else { this.dismiss(); } }); }; dismiss = _ => { this.props.navigator.pop(); if (Platform.OS === "android") { ToastAndroid.show(this.props.successMessage, ToastAndroid.SHORT); } }; isValid() { const { questions } = this.props.survey; return Object.keys(this.state).length === questions.length; } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.white }, header: { alignItems: "center", paddingVertical: 30, paddingHorizontal: 22 }, // subheading:{ // fontFamily: F8Fonts.helvetica, // fontSize: 15, // color: F8Colors.blue, // marginBottom: 15 // }, heading: { textAlign: "center", color: F8Colors.blue // marginBottom: 15, }, description: { textAlign: "center", marginTop: 12 }, question: { paddingHorizontal: 22, // was:30 marginBottom: 30 }, footer: { paddingHorizontal: 22, paddingBottom: 30 } }); function select(store) { return { sessions: store.sessions }; } module.exports = connect(select)(RatingScreen); ================================================ FILE: js/rating/StarRating.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, TouchableOpacity, Image } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import { Text } from "../common/F8Text"; /* ============================================================================= */ function StarRating({ labels, rating, onChange }) { const stars = [1, 2, 3, 4, 5].map(value => ( onChange(value)} /> )); let labelSection; if (labels && labels.low && labels.high) { labelSection = ( {labels.low} {labels.high} ); } return ( {stars} {labelSection} ); } /* ============================================================================= */ function Star({ isFull, value, onPress }) { const source = isFull ? require("./img/star-active.png") : require("./img/star-default.png"); const accessibilityTraits = ["button"]; if (isFull) { accessibilityTraits.push("selected"); } return ( ); } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { alignSelf: "center", width: 298 }, stars: { flexDirection: "row", justifyContent: "space-between", width: 260, // backgroundColor: 'yellow', alignSelf: "center" }, star: { flex: 1, alignItems: "center" }, labels: { // paddingHorizontal: 14, paddingTop: 20, flexDirection: "row", justifyContent: "space-between" }, label: { fontSize: 11, width: 90, color: "rgba(3,34,80,1)", textAlign: "center" // backgroundColor: 'red' } }); /* exports & Playground cards =============================================== */ module.exports = StarRating; module.exports.__cards = define => { define("Default", (state = null, update) => ( )); }; ================================================ FILE: js/rating/StarRatings.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import F8Fonts from "../common/F8Fonts"; import { View } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; import { Text } from "../common/F8Text"; import StarRating from "./StarRating"; /* StarRatings =============================================================== */ class StarRatings extends React.Component { static defaultProps = { rows: [] }; constructor(props) { super(props); this.state = {}; } componentWillUpdate(nextProps, nextState) { if (nextState !== this.state) { const { rows } = this.props; if (Object.keys(nextState).length === rows.length) { const answers = rows.map((_, ii) => nextState[ii]); this.props.onChange && this.props.onChange(answers); } } } render() { const { rows, labels } = this.props; if (!rows.length) { return null; } const content = rows.map((row, idx) => ( {row.toUpperCase()} this.setState({ [idx]: val })} /> )); return {content}; } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ row: { marginVertical: 15 }, heading: { fontFamily: F8Fonts.helvetica, fontSize: F8Fonts.normalize(15), textAlign: "center", color: F8Colors.blue, marginBottom: 17 } }); /* exports & Playground cards =============================================== */ module.exports = StarRatings; module.exports.__cards = define => { define("Default", (state = null, update) => ( )); }; ================================================ FILE: js/rating/WrittenResponse.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../common/F8Colors"; import F8Fonts from "../common/F8Fonts"; import { Platform, View, TextInput, Alert } from "react-native"; import StyleSheet from "../common/F8StyleSheet"; /* ============================================================================= */ class WrittenResponse extends React.Component { static defaultProps = { multiline: true, maxLength: 500, inputColor: F8Colors.coolGray, textColor: F8Colors.tangaroa, onChange: _ => {} }; render() { const { onChange, multiline, maxLength, placeholder, inputColor, textColor } = this.props; let platformStyles; if (Platform.OS === "ios") { platformStyles = { borderColor: inputColor, color: textColor }; } else { platformStyles = { color: textColor }; } return ( onChange(event.nativeEvent.text)} /> ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ wrapper: { flex: 1, justifyContent: "center", paddingVertical: 15 }, input: { fontFamily: F8Fonts.default, fontSize: F8Fonts.normalize(17), color: F8Colors.tangaroa, height: 100, ios: { borderRadius: 5, borderWidth: 1, borderColor: F8Colors.coolGray, paddingVertical: 15, paddingHorizontal: 20 } } }); /* exports & Playground cards =============================================== */ module.exports = WrittenResponse; module.exports.__cards = define => { define("Written", _ => ( Alert.alert(value)} /> )); }; ================================================ FILE: js/reducers/__mocks__/parse.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; class ParseObjectMock { id: string; createdAt: Date; _fields: Object; constructor(fields: Object) { this._fields = fields; this.id = Math.ceil(Math.random() * 0xffffff).toString(16); this.createdAt = new Date(); } get(name: string): any { return this._fields[name]; } } class ParseFileMock { _url: string; constructor(url: string) { this._url = url; } url(): string { return this._url; } } module.exports = { Object: ParseObjectMock, File: ParseFileMock }; ================================================ FILE: js/reducers/__tests__/maps-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; jest.autoMockOff(); import Parse from "parse"; import maps from "../maps"; describe("maps reducer", () => { it("is empty by default", () => { expect(maps(undefined, {})).toEqual([]); }); it("populates maps from server", () => { let list = [ new Parse.Object({ name: "Day 1", x1: new Parse.File("x1.png"), x2: new Parse.File("x2.png"), x3: new Parse.File("x3.png") }) ]; expect(maps([], { type: "LOADED_MAPS", list })).toEqual([ { id: jasmine.any(String), name: "Day 1", x1url: "x1.png", x2url: "x2.png", x3url: "x3.png" } ]); }); }); ================================================ FILE: js/reducers/__tests__/notifications-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Parse from "parse"; jest.dontMock("../notifications"); jest.dontMock("crc32"); import notifications from "../notifications"; const emptyAction: any = {}; const empty = { server: [], push: [], enabled: null, registered: false, seen: {} }; describe("notifications reducer", () => { it("is empty by default", () => { expect(notifications(undefined, emptyAction)).toEqual(empty); }); it("populates notifications from server", () => { let list = [ new Parse.Object({ text: "hello", url: "https://fbf8.com" }), new Parse.Object({ text: "bye", url: null }) ]; let { server } = notifications(empty, { type: "LOADED_NOTIFICATIONS", list }); expect(server).toEqual([ { id: jasmine.any(String), text: "hello", url: "https://fbf8.com", time: jasmine.any(Number) }, { id: jasmine.any(String), text: "bye", url: null, time: jasmine.any(Number) } ]); }); it("skips duplicates", () => { const notification = { text: "Hello, world!", url: null, time: 1234567 }; const action1 = { type: "RECEIVED_PUSH_NOTIFICATION", notification: { ...notification } }; const action2 = { type: "RECEIVED_PUSH_NOTIFICATION", notification: { ...notification } }; const { push } = notifications(notifications(empty, action1), action2); expect(push).toEqual([ { id: jasmine.any(String), ...notification } ]); }); }); ================================================ FILE: js/reducers/__tests__/schedule-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; jest.dontMock("../schedule"); import schedule from "../schedule"; describe("schedule reducer", () => { it("is empty by default", () => { expect(schedule(undefined, ({}: any))).toEqual({}); }); it("adds sessions to schedule", () => { expect(schedule({}, { type: "SESSION_ADDED", id: "one" })).toEqual({ one: true }); expect( schedule({ one: true }, { type: "SESSION_ADDED", id: "two" }) ).toEqual({ one: true, two: true }); }); it("removes sessions from schedule", () => { expect( schedule( { one: true, two: true }, { type: "SESSION_REMOVED", id: "two" } ) ).toEqual({ one: true }); }); it("restores schedule when logging in", () => { expect( schedule( { one: true }, { type: "RESTORED_SCHEDULE", list: [{ id: "two" }, { id: "three" }] } ) ).toEqual({ two: true, three: true }); }); it("clears schedule when logging out", () => { expect( schedule( { one: true, two: true }, { type: "LOGGED_OUT" } ) ).toEqual({}); }); }); ================================================ FILE: js/reducers/config.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import type { Action } from "../actions/types"; export type Config = { wifiNetwork: string, wifiPassword: string, appLinkURL: string, appInvitePreviewImageURL: string, sessionURLTemplate: string, thirdPartyNotices: string, manageBookingsURL: string }; const initialState: Config = { wifiNetwork: "", wifiPassword: "", appLinkURL: "https://www.fbf8.com/", appInvitePreviewImageURL: "", sessionURLTemplate: "https://www.fbf8.com/schedule/session/{slug}", thirdPartyNotices: "", manageBookingsURL: "https://demos.fbf8.com/manage-reservations" }; function config(state: Config = initialState, action: Action): Config { if (action.type === "LOADED_CONFIG") { return { wifiNetwork: action.config.get("wifiNetwork") || state.wifiNetwork, wifiPassword: action.config.get("wifiPassword") || state.wifiPassword, appLinkURL: action.config.get("appLinkURL") || state.appLinkURL, appInvitePreviewImageURL: action.config.get("appInvitePreviewImageURL") || state.appInvitePreviewImageURL, sessionURLTemplate: action.config.get("sessionURLTemplate") || state.sessionURLTemplate, manageBookingsURL: action.config.get("manageBookingsURL") || state.manageBookingsURL }; } return state; } module.exports = config; ================================================ FILE: js/reducers/createParseReducer.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; type Convert = (object: Object) => T; type Reducer = (state: ?Array, action: any) => Array; function createParseReducer(type: string, convert: Convert): Reducer { return function(state: ?Array, action: Action): Array { if (action.type === type) { // Flow can't guarantee {type, list} is a valid action return (action: any).list.map(convert); } return state || []; }; } module.exports = createParseReducer; ================================================ FILE: js/reducers/faqs.js ================================================ /** * Copyright 2014 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import createParseReducer from "./createParseReducer"; export type FAQ = { id: string, question: string, answer: string }; function fromParseObject(map: Object): FAQ { return { id: map.id, question: map.get("question"), answer: map.get("answer") }; } module.exports = createParseReducer("LOADED_FAQS", fromParseObject); ================================================ FILE: js/reducers/friendsSchedules.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type FriendsSchedule = { id: string, name: string, schedule: { [key: string]: boolean } }; type State = Array; function friendsSchedules(state: State = [], action: Action): State { if (action.type === "LOADED_FRIENDS_SCHEDULES") { return action.list; } if (action.type === "LOGGED_OUT") { return []; } return state; } module.exports = friendsSchedules; ================================================ FILE: js/reducers/index.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { combineReducers } from "redux"; module.exports = combineReducers({ config: require("./config"), notifications: require("./notifications"), maps: require("./maps"), sessions: require("./sessions"), user: require("./user"), schedule: require("./schedule"), scheduleTopics: require("./scheduleTopics"), scheduleFilter: require("./scheduleFilter"), faqs: require("./faqs"), pages: require("./pages"), navigation: require("./navigation"), friendsSchedules: require("./friendsSchedules"), surveys: require("./surveys"), videos: require("./videos"), videoTopics: require("./videoTopics"), videoFilter: require("./videoFilter"), policies: require("./policies"), testEventDates: require("./testEventDates") }); ================================================ FILE: js/reducers/maps.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import createParseReducer from "./createParseReducer"; export type Map = { id: string, name: string, x1url: string, x2url: string, x3url: string, width: number, height: number }; function fromParseObject(map: Object): Map { return { id: map.id, name: map.get("name"), x1url: map.get("x1") && map.get("x1").url(), x2url: map.get("x2") && map.get("x2").url(), x3url: map.get("x3") && map.get("x3").url(), width: map.get("width"), height: map.get("height") }; } module.exports = createParseReducer("LOADED_MAPS", fromParseObject); ================================================ FILE: js/reducers/navigation.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type Tab = "schedule" | "my-schedule" | "map" | "notifications" | "info"; export type Day = 1 | 2; type State = { tab: Tab, day: Day }; const initialState: State = { tab: "schedule", day: 1 }; function navigation(state: State = initialState, action: Action): State { if (action.type === "SWITCH_TAB") { return { ...state, tab: action.tab }; } if (action.type === "SWITCH_DAY") { return { ...state, day: action.day }; } if (action.type === "LOGGED_OUT") { return initialState; } return state; } module.exports = navigation; ================================================ FILE: js/reducers/notifications.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { Platform } from "react-native"; import crc32 from "crc32"; export type Notification = { id: string, url: ?string, urlTitle: ?string, text: string, time: number, image: ?string }; export type SeenNotifications = { [id: string]: boolean }; type State = { enabled: ?boolean, // null = no answer registered: boolean, // Most notifications will be stored on Parse Core, so that // people who installed the app after the conference started can // get access. But some notifications will be delivered // via push and only to subset of attendees. server: Array, push: Array, seen: SeenNotifications }; const initialState = { server: [], push: [], enabled: Platform.OS === "ios" ? null : true, registered: false, seen: {} }; import type { Action } from "../actions/types"; function notifications(state: State = initialState, action: Action): State { switch (action.type) { case "LOADED_NOTIFICATIONS": let list = action.list.map(fromParseObject); return { ...state, server: list }; case "RECEIVED_PUSH_NOTIFICATION": return { ...state, push: append(action.notification, state.push) }; case "LOGGED_OUT": return { ...state, push: [] }; case "TURNED_ON_PUSH_NOTIFICATIONS": return { ...state, enabled: true }; case "SKIPPED_PUSH_NOTIFICATIONS": return { ...state, enabled: false }; case "REGISTERED_PUSH_NOTIFICATIONS": return { ...state, registered: true }; case "RESET_NUXES": return { ...state, enabled: initialState.enabled }; case "SEEN_ALL_NOTIFICATIONS": return { ...state, seen: fetchAllIds([...state.server, ...state.push]) }; default: return state; } } function append(notification, list) { const id = notification.id || crc32(notification.text + notification.url).toString(36); if (list.find(n => n.id === id)) { return list; } return [{ id, ...notification }, ...list]; } function fetchAllIds(notifs: Array): SeenNotifications { const seen = {}; notifs.forEach(notification => { seen[notification.id] = true; }); return seen; } function fromParseObject(object: Object): Notification { return { id: object.id, text: object.get("text"), url: object.get("url"), urlTitle: object.get("urlTitle"), time: object.createdAt.getTime(), image: object.get("image") }; } module.exports = notifications; ================================================ FILE: js/reducers/pages.js ================================================ /** * Copyright 2014 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import createParseReducer from "./createParseReducer"; export type Page = { id: string, title: string, url: string, logo: ?string }; function fromParseObject(map: Object): Page { return { id: map.id, title: map.get("title"), url: map.get("url"), logo: map.get("logo") && map.get("logo").url() }; } module.exports = createParseReducer("LOADED_PAGES", fromParseObject); ================================================ FILE: js/reducers/policies.js ================================================ /** * Copyright 2014 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import createParseReducer from "./createParseReducer"; export type Policy = { id: string, title: string, url: string }; function fromParseObject(policy: Object): Policy { return { id: policy.id, title: policy.get("title"), url: policy.get("url") }; } module.exports = createParseReducer("LOADED_POLICIES", fromParseObject); ================================================ FILE: js/reducers/schedule.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type State = { [id: string]: boolean }; function schedule(state: State = {}, action: Action): State { switch (action.type) { case "SESSION_ADDED": let added = {}; added[action.id] = true; return { ...state, ...added }; case "SESSION_REMOVED": let rest = { ...state }; delete rest[action.id]; return rest; case "LOGGED_OUT": return {}; case "RESTORED_SCHEDULE": let all = {}; action.list.forEach(session => { all[session.id] = true; }); return all; } return state; } module.exports = schedule; ================================================ FILE: js/reducers/scheduleFilter.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type FriendFilter = { id: string, name: string, schedule: { [key: string]: boolean } }; export type TopicsFilter = { [key: string]: boolean }; type State = TopicsFilter; function filter(state: State = {}, action: Action): State { if (action.type === "APPLY_SCHEDULE_TOPICS_FILTER") { return action.scheduleTopics; } if (action.type === "CLEAR_SCHEDULE_FILTER") { return {}; } return state; } module.exports = filter; ================================================ FILE: js/reducers/scheduleTopics.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; type State = Array; type Action = { type: string, list: Array }; function topics(state: State = [], action: Action): State { if (action.type === "LOADED_SESSIONS") { const topicsMap = Object.create(null); action.list.forEach(session => { const tags = session.get("tags") || []; tags.forEach(tag => { topicsMap[tag] = true; }); }); return Object.keys(topicsMap).sort(); } return state; } module.exports = topics; ================================================ FILE: js/reducers/sessions.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { parseTimeToUTC } from "../common/convertTimes"; import createParseReducer from "./createParseReducer"; export type Speaker = { id: string, bio: string, name: string, pic: string, title: string }; export type Session = { id: string, day: number, allDay: boolean, title: string, description: string, hasDetails: boolean, slug: string, speakers: Array, onMySchedule: boolean, tags: Array, startTime: number, endTime: number, map: ?string, location: ?string }; function fromParseSpeaker(speaker: Object): Speaker { const pic = speaker.get("speakerPic"); return { id: speaker.id, bio: speaker.get("speakerBio"), name: speaker.get("speakerName"), pic: pic && pic.url(), title: speaker.get("speakerTitle") }; } function fromParseSessions(session: Object): Session { return { id: session.id, day: session.get("day"), allDay: session.get("allDay"), title: session.get("sessionTitle"), description: session.get("sessionDescription"), hasDetails: session.get("hasDetails"), slug: session.get("sessionSlug"), speakers: (session.get("speakers") || []).map(fromParseSpeaker), onMySchedule: session.get("onMySchedule"), tags: session.get("tags") || [], startTime: session.get("startTime") && session.get("startTime").getTime() && parseTimeToUTC(session.get("startTime").getTime()), endTime: session.get("endTime") && session.get("endTime").getTime() && parseTimeToUTC(session.get("endTime").getTime()), map: session.get("sessionMap") && session.get("sessionMap").url(), location: session.get("sessionLocation") }; } module.exports = createParseReducer("LOADED_SESSIONS", fromParseSessions); ================================================ FILE: js/reducers/surveys.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type Question = { text: string, lowLabel: string, highLabel: string }; export type Survey = { id: string, sessionId: string, questions: Array }; type State = Array; function surveys(state: State = [], action: Action): State { if (action.type === "LOADED_SURVEYS") { return action.list; } if (action.type === "SUBMITTED_SURVEY_ANSWERS") { const submittedSurveyId = action.id; return state.filter(survey => survey.id !== submittedSurveyId); } if (action.type === "LOGGED_OUT") { return []; } return state; } module.exports = surveys; ================================================ FILE: js/reducers/testEventDates.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; type State = ?number; function setCurrentDateForTesting(state: State = null, action: Action): State { if (action.type === "SET_TIMED_TESTING") { return action.value; } return state; } module.exports = setCurrentDateForTesting; ================================================ FILE: js/reducers/user.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; export type State = { isLoggedIn: boolean, hasSkippedLogin: boolean, sharedSchedule: ?boolean, id: ?string, name: ?string }; const initialState = { isLoggedIn: false, hasSkippedLogin: false, sharedSchedule: null, id: null, name: null }; function user(state: State = initialState, action: Action): State { if (action.type === "LOGGED_IN") { let { id, name, sharedSchedule } = action.data; if (sharedSchedule === undefined) { sharedSchedule = null; } return { isLoggedIn: true, hasSkippedLogin: false, sharedSchedule, id, name }; } if (action.type === "SKIPPED_LOGIN") { return { ...initialState, hasSkippedLogin: true }; } if (action.type === "LOGGED_OUT") { return initialState; } if (action.type === "SET_SHARING") { return { ...state, sharedSchedule: action.enabled }; } if (action.type === "RESET_NUXES") { return { ...state, sharedSchedule: null }; } return state; } module.exports = user; ================================================ FILE: js/reducers/videoFilter.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Action } from "../actions/types"; // export type FriendFilter = { // id: string; // name: string; // video: {[key: string]: boolean}; // }; export type TopicsFilter = { [key: string]: boolean }; type State = TopicsFilter; function filter(state: State = {}, action: Action): State { if (action.type === "APPLY_VIDEO_TOPICS_FILTER") { return action.videoTopics; } if (action.type === "CLEAR_VIDEO_FILTER") { return {}; } return state; } module.exports = filter; ================================================ FILE: js/reducers/videoTopics.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; type State = Array; type Action = { type: string, list: Array }; function topics(state: State = [], action: Action): State { if (action.type === "LOADED_VIDEOS") { const topicsMap = Object.create(null); action.list.forEach(video => { const tags = video.get("tags") || []; tags.forEach(tag => { topicsMap[tag] = true; }); }); return Object.keys(topicsMap).sort(); } return state; } module.exports = topics; ================================================ FILE: js/reducers/videos.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import createParseReducer from "./createParseReducer"; export type Video = { id: string, source: string, title: string, description: string, image: ?string, year: number, length: string, tags: Array, shareURL: ?string, featured: ?boolean // survey/rating/review? }; function fromParseVideos(video: Object): Video { return { id: video.id, source: video.get("source"), title: video.get("title"), description: video.get("description"), image: video.get("image"), year: video.get("year"), length: video.get("length"), tags: video.get("tags") || [], shareURL: video.get("shareURL"), featured: video.get("featured") }; } module.exports = createParseReducer("LOADED_VIDEOS", fromParseVideos); ================================================ FILE: js/relay-environment.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ import { Environment, Network, RecordSource, Store } from "relay-runtime"; import { graphqlURL } from "./env"; const source = new RecordSource(); const store = new Store(source); function fetchQuery(operation, variables, cacheConfig, uploadables) { return fetch(graphqlURL, { method: "POST", headers: { Accept: "application/json", "content-type": "application/json" }, body: JSON.stringify({ query: operation.text, variables }) }).then(response => { return response.json(); }); } const network = Network.create(fetchQuery); module.exports = new Environment({ network, store }); ================================================ FILE: js/setup.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; // Depdencies import React from "react"; import FacebookSDK from "./FacebookSDK"; import Parse from "parse/react-native"; import configureStore from "./store/configureStore"; import { Provider } from "react-redux"; // Components import { Text, AsyncStorage } from "react-native"; import F8App from "./F8App"; import LaunchScreen from "./common/LaunchScreen"; // Config import { serverURL, parseAppID } from "./env"; function setup(): ReactClass<{}> { console.disableYellowBox = true; Parse.setAsyncStorage(AsyncStorage); Parse.initialize(parseAppID); Parse.serverURL = `${serverURL}/parse`; console.log("DEBUG!!! " + serverURL); FacebookSDK.init(); Parse.FacebookUtils.init(); // TODO: Don't prevent fontScaling on iOS (currently breaks UI) Text.defaultProps.allowFontScaling = false; class Root extends React.Component { state: { isLoading: boolean, store: any }; constructor() { super(); this.state = { storeCreated: false, storeRehydrated: false, store: null }; } componentDidMount() { configureStore( // rehydration callback (after async compatibility and persistStore) _ => this.setState({ storeRehydrated: true }) ).then( // creation callback (after async compatibility) store => this.setState({ store, storeCreated: true }) ); } render() { if (!this.state.storeCreated || !this.state.storeRehydrated) { return ; } return ( ); } } return Root; } module.exports = setup; ================================================ FILE: js/store/analytics.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import track from "./track"; module.exports = store => next => action => { track(action); return next(action); }; ================================================ FILE: js/store/array.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; module.exports = store => next => action => Array.isArray(action) ? action.map(next) : next(action); ================================================ FILE: js/store/compatibility.js ================================================ import { AsyncStorage } from "react-native"; import { compatibleStoreVersion } from "../env"; const LS_GROUP = "F8StoreCompatibility", LS_VERSIONING = `${LS_GROUP}:version`; export async function ensureCompatibility() { try { const stored = await AsyncStorage.getItem(LS_VERSIONING); if (stored && stored === JSON.stringify(compatibleStoreVersion)) { return false; // no need to update } } catch (error) {} return await resetCompatibility(); } async function resetCompatibility() { try { const keys = await AsyncStorage.getAllKeys(); // force clear everything except for versioning (all reduxPersist:x and Parse:x keys) const targets = (keys || []).filter(k => k !== LS_VERSIONING); if (targets.length) { await AsyncStorage.multiRemove(targets); } // after storage reset, update the compatibility to the current storage version return await updateCompatibility(); } catch (error) {} return false; } async function updateCompatibility() { try { await AsyncStorage.setItem( LS_VERSIONING, JSON.stringify(compatibleStoreVersion) ); return true; } catch (error) {} return false; } ================================================ FILE: js/store/configureStore.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import { applyMiddleware, createStore } from "redux"; import thunk from "redux-thunk"; import promise from "./promise"; import array from "./array"; import analytics from "./analytics"; import reducers from "../reducers"; import createLogger from "redux-logger"; import { persistStore, autoRehydrate } from "redux-persist"; import { AsyncStorage } from "react-native"; import { ensureCompatibility } from "./compatibility"; const isDebuggingInChrome = false; const logger = createLogger({ predicate: (getState, action) => isDebuggingInChrome, collapsed: true, duration: true }); const createF8Store = applyMiddleware(thunk, promise, array, analytics, logger)( createStore ); async function configureStore(onComplete: ?() => void) { const didReset = await ensureCompatibility(); const store = autoRehydrate()(createF8Store)(reducers); persistStore(store, { storage: AsyncStorage }, _ => onComplete(didReset)); if (isDebuggingInChrome) { window.store = store; } return store; } module.exports = configureStore; ================================================ FILE: js/store/promise.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import { testMenuEnabled } from "../env"; function warn(error) { if (testMenuEnabled) { console.warn(error.message || error); } // only log promise failures when debug menu is enabled throw error; // To let the caller handle the rejection } module.exports = store => next => action => typeof action.then === "function" ? Promise.resolve(action).then(next, warn) : next(action); ================================================ FILE: js/store/track.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Analytics from "../F8Analytics"; import type { Action } from "../actions/types"; function track(action: Action): void { switch (action.type) { case "LOGGED_IN": F8Analytics.logEvent("Login", 1, { source: action.source || "" }); break; case "LOGGED_OUT": F8Analytics.logEvent("Logout", 1); break; case "SKIPPED_LOGIN": F8Analytics.logEvent("Skip login", 1); break; case "SESSION_ADDED": F8Analytics.logEvent("Added To Schedule", 1, { id: action.id }); break; case "SESSION_REMOVED": F8Analytics.logEvent("Removed From Schedule", 1, { id: action.id }); break; case "TURNED_ON_PUSH_NOTIFICATIONS": F8Analytics.logEvent("Enabled Push", 1); break; case "SKIPPED_PUSH_NOTIFICATIONS": F8Analytics.logEvent("Disabled Push", 1); break; case "SET_SHARING": F8Analytics.logEvent( action.enabled ? "Enabled Sharing" : "Disabled Sharing", 1 ); break; case "APPLY_SCHEDULE_TOPICS_FILTER": F8Analytics.logEvent("Filtered Schedule", 1); break; case "APPLY_VIDEO_TOPICS_FILTER": F8Analytics.logEvent("Filtered Videos", 1); break; } } module.exports = track; ================================================ FILE: js/tabs/F8TabsView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { connect } from "react-redux"; import type { Tab, Day } from "../reducers/navigation"; import { switchTab } from "../actions"; import unseenNotificationsCount from "./notifications/unseenNotificationsCount"; import F8Fonts from "../common/F8Fonts"; import F8Colors from "../common/F8Colors"; import StyleSheet from "../common/F8StyleSheet"; import { PixelRatio, Platform, View, Text, Image, StatusBar } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import F8InfoView from "./info/F8InfoView"; import MyScheduleView from "./schedule/MyScheduleView"; import GeneralScheduleView from "./schedule/GeneralScheduleView"; import F8VideosView from "./videos/F8VideosView"; import TabNavigator from "react-native-tab-navigator"; import F8DemosView from "./demos/F8DemosView"; import { currentTimeOnConferenceDay } from "../common/convertTimes"; /* constants ============================================================================= */ const SCHEDULE_ICONS = { day1: { default: require("./schedule/img/tab-icon/1/default.png"), active: require("./schedule/img/tab-icon/1/active.png") }, day2: { default: require("./schedule/img/tab-icon/2/default.png"), active: require("./schedule/img/tab-icon/2/active.png") } }; const BADGE_SIZE = 14, BADGE_PADDING_H = 3, UPDATE_LOOP_MINUTES = 1, UPDATE_LOOP_DURATION = UPDATE_LOOP_MINUTES * 60 * 1000; // convert ms; /* ============================================================================= -------------------------------------------------------------------------------- Props: coming soon ============================================================================= */ class F8TabsView extends React.Component { props: { tab: Tab, day: Day, onTabSelect: (tab: Tab) => void, navigator: Navigator }; constructor(props) { super(props); this.state = { now: props.presetDate ? currentTimeOnConferenceDay(props.presetDate) : new Date().getTime() }; } componentDidMount() { if (Platform.OS === "ios") { StatusBar && StatusBar.setBarStyle("light-content"); // TODO: VIDEO FIX? } clearInterval(this.updateLoop); this.updateLoop = setInterval(_ => { const now = this.props.presetDate ? currentTimeOnConferenceDay(this.props.presetDate) : new Date().getTime(); this.setState({ now }); }, UPDATE_LOOP_DURATION); } componentWillReceiveProps(nextProps) { if (nextProps.presetDate !== this.props.presetDate) { const now = nextProps.presetDate ? currentTimeOnConferenceDay(nextProps.presetDate) : new Date().getTime(); this.setState({ now }); } } componentWillUnmount() { clearInterval(this.updateLoop); } onTabSelect(tab: Tab) { if (this.props.tab !== tab) { this.props.onTabSelect(tab); } } render() { let scheduleIcon = SCHEDULE_ICONS.day1.default; // day 1 and fallback let scheduleIconSelected = SCHEDULE_ICONS.day1.active; // day 1 and fallback if (this.props.day === 2) { scheduleIcon = SCHEDULE_ICONS.day2.default; scheduleIconSelected = SCHEDULE_ICONS.day2.active; } return ( this.renderTabIcon(scheduleIcon)} renderSelectedIcon={_ => this.renderTabIcon(scheduleIconSelected)} > this.renderTabIcon( require("./schedule/img/tab-icon/my-f8/default.png") )} renderSelectedIcon={_ => this.renderTabIcon( require("./schedule/img/tab-icon/my-f8/active.png") )} > this.renderTabIcon(require("./demos/img/tab-icon/default.png"))} renderSelectedIcon={_ => this.renderTabIcon(require("./demos/img/tab-icon/active.png"))} > this.renderTabIcon(require("./videos/img/tab-icon/default.png"))} renderSelectedIcon={_ => this.renderTabIcon(require("./videos/img/tab-icon/active.png"))} > } renderIcon={_ => this.renderTabIcon(require("./info/img/tab-icon/default.png"))} renderSelectedIcon={_ => this.renderTabIcon(require("./info/img/tab-icon/active.png"))} > ); } renderTabIcon(src) { return ( ); } } class TabBadge extends React.Component { render() { if (!this.props.value) { return null; } const len = String(this.props.value).length; let sizing; if (len > 1) { sizing = styles.badgeFlexible; } else { sizing = styles.badgeFixed; } return ( {this.props.value} ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ tabBar: { borderTopWidth: 1 / PixelRatio.get(), borderTopColor: F8Colors.magnesium, backgroundColor: F8Colors.lightBackground }, tabTitle: { backgroundColor: "transparent", // fontFamily: OC3Fonts.regular, fontSize: 10, color: F8Colors.colorWithAlpha("sapphire", 0.65) }, tabTitleActive: { color: F8Colors.sapphire }, badge: { position: "absolute", right: -5, top: 2, backgroundColor: F8Colors.pink, // borderWidth:1, // borderColor: F8Colors.pink, borderRadius: BADGE_SIZE / 2, height: BADGE_SIZE, alignItems: "center", justifyContent: "center" }, badgeFixed: { width: BADGE_SIZE }, badgeFlexible: { paddingHorizontal: BADGE_PADDING_H }, badgeText: { backgroundColor: "transparent", fontSize: 9, fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "helveticaBold"), color: F8Colors.white, ios: { lineHeight: 10 } }, // icons =================== iconWrapper: { width: 28, height: 30, alignItems: "center", justifyContent: "center", bottom: -3 // backgroundColor:'rgba(0,0,0,1)', // ios: { bottom: -3 }, // android: { bottom: -4 }, }, tabIcon: { // backgroundColor:'rgba(255,255,0,1)', } }); /* Selectors ============================================================================= */ function select(store) { return { tab: store.navigation.tab, day: store.navigation.day, presetDate: store.testEventDates, notificationsBadge: unseenNotificationsCount(store) }; } function actions(dispatch) { return { onTabSelect: tab => dispatch(switchTab(tab)) }; } /* Export ============================================================================= */ module.exports = connect(select, actions)(F8TabsView); ================================================ FILE: js/tabs/MenuItem.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import F8Colors from "../../common/F8Colors"; import React from "react"; import View from "View"; import { Text } from "../common/F8Text"; import F8Touchable from "../../common/F8Touchable"; import Image from "Image"; import StyleSheet from "StyleSheet"; class MenuItem extends React.Component { props: { icon: number, selectedIcon: number, selected: boolean, title: string, badge: ?string, onPress: () => void }; render() { let icon = this.props.selected ? this.props.selectedIcon : this.props.icon; const selectedTitleStyle = this.props.selected && styles.selectedTitle; let badge; if (this.props.badge) { badge = ( {this.props.badge} ); } return ( {this.props.title} {badge} ); } } const styles = StyleSheet.create({ container: { flexDirection: "row", height: 50, alignItems: "center", paddingHorizontal: 20 }, icon: { marginRight: 20 }, title: { flex: 1, fontSize: 17, color: F8Colors.lightText }, selectedTitle: { color: F8Colors.darkText }, badge: { backgroundColor: "#DC3883", paddingHorizontal: 10, paddingVertical: 2, borderRadius: 10 }, badgeText: { fontSize: 12, color: "white" } }); module.exports = MenuItem; ================================================ FILE: js/tabs/demos/DemosCarousel.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Analytics from "../../F8Analytics"; import { View } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import F8DemoDetails from "./F8DemoDetails"; import F8Header from "../../common/F8Header"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import StyleSheet from "../../common/F8StyleSheet"; import Carousel from "../../common/Carousel"; import { HeaderTitle } from "../../common/F8Text"; import F8PageControl from "../../common/F8PageControl"; import { connect } from "react-redux"; type Props = { allDemos: Array, navigator: Navigator }; class DemosCarousel extends React.Component { props: Props; state: { selectedIndex: number }; static defaultProps = { title: "Demos" }; constructor(props) { super(props); this.state = { selectedIndex: props.selectedIndex }; (this: any).handleIndexChange = this.handleIndexChange.bind(this); (this: any).renderCard = this.renderCard.bind(this); } render() { const backItem = { title: "Back", layout: "icon", icon: require("../../common/img/header/back.png"), onPress: () => this.props.navigator.pop() }; return ( {this.props.title} ); } renderCard(index: number): ReactElement { return ( ); } componentDidMount() { this.track(this.state.selectedIndex); } handleIndexChange(selectedIndex: number) { this.track(selectedIndex); this.setState({ selectedIndex }); } track(index: number) { const { id } = this.props.allDemos[index]; F8Analytics.logEvent("View Demo", 1, { id }); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.white }, headerContent: { android: { flex: 1, alignItems: "flex-start", justifyContent: "center" }, ios: { alignItems: "center", justifyContent: "center" } }, day: { color: F8Colors.yellow, fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "helveticaBold"), fontSize: 13 }, time: { color: F8Colors.white, fontFamily: F8Fonts.helvetica, fontSize: 15 } }); module.exports = connect()(DemosCarousel); ================================================ FILE: js/tabs/demos/F8DemoDetails.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { connect } from "react-redux"; import { Dimensions, Image, View, TouchableOpacity, ScrollView } from "react-native"; import { Text, Heading2, Heading4, Paragraph } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; import F8Colors from "../../common/F8Colors"; import F8Button from "../../common/F8Button"; import ActionsOverlay from "../../common/ActionsOverlay"; import F8ScrollingHeader from "../../common/F8ScrollingHeader"; import MapView from "../../common/MapView"; import Carousel from "../../common/Carousel"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, HORIZONTAL_BREAKPOINT = WINDOW_WIDTH <= 320, CONTENT_PADDING_H = HORIZONTAL_BREAKPOINT ? 20 : 30; /* ============================================================================= ============================================================================= */ const F8DemoDetails = React.createClass({ getInitialState: function() { return { scrollTop: 0 }; }, render: function() { const { demo, map } = this.props; const paddingBottom = demo.booking ? 110 : 30; return ( this.setState({ scrollTop: nativeEvent.contentOffset.y })} scrollEventThrottle={100} showsVerticalScrollIndicator={false} automaticallyAdjustContentInsets={false} > {this.renderHeading(demo)} {this.renderDescription(demo.description)} {this.renderMap(map)} {this.renderLinksSection(demo.links)} {this.renderActions(demo.booking)} {this.renderMiniHeader(demo.title)} ); }, renderHeading({ title, logo, logoWidth, logoHeight }) { if (logo && logoWidth && logoHeight) { return ( ); } else if (title) { return {title}; } else { return null; } }, renderDescription(description) { if (description) { return {description}; } else { return null; } }, renderMap(map) { if (map) { const mapWidth = Carousel.CardWidth - CONTENT_PADDING_H * 2; return ; } else { return null; } }, renderLinksSection(links) { if (!links || !links.length) { return null; } const content = links.map((link, idx) => { if (link.title && link.url) { return ( this.props.navigator && this.props.navigator.push({ webview: link.url, backgroundColor: F8Colors.turquoise, titleColor: F8Colors.white, itemsColor: F8Colors.white })} > {link.title} ); } }); return
{content}
; }, // "Book Now" floating actions if necessary renderActions(bookingURL) { if (!bookingURL) { return null; } return ( { this.props.navigator && this.props.navigator.push({ webview: bookingURL, backgroundColor: F8Colors.turquoise, titleColor: F8Colors.white, itemsColor: F8Colors.white }); }} /> ); }, // Small header that shows/hides at scroll y offset trigger renderMiniHeader(title) { return ( ); } }); class Section extends React.Component { props: { title?: string, children?: any }; render() { const { children } = this.props; if (React.Children.count(children) === 0) { return null; } let header; if (this.props.title) { header = ( {this.props.title.toUpperCase()} ); } return ( {header} {children} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "white" }, contentContainer: { paddingTop: 25, paddingHorizontal: CONTENT_PADDING_H }, title: { color: F8Colors.blue, marginTop: 5, marginBottom: 20 }, logo: { marginTop: 5, marginBottom: 20 }, section: { marginTop: 30 }, sectionTitle: { marginBottom: 8 }, actions: { position: "absolute", left: 0, right: 0, bottom: 0 }, map: { marginTop: 32 }, link: { paddingVertical: 10, flexDirection: "row", alignItems: "center" }, linkText: { flex: 1, paddingRight: 10, alignSelf: "flex-start" }, linkArrow: { flex: 0, alignSelf: "flex-end" } }); /* data store =============================================================== */ function select(store, props) { const map = store.maps.find(({ name }) => name === props.demo.location); return { map }; } /* exports ================================================================== */ module.exports = connect(select)(F8DemoDetails); ================================================ FILE: js/tabs/demos/F8DemosView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { QueryRenderer, graphql } from "react-relay"; import idx from "idx"; import { connect } from "react-redux"; import ListContainer from "../../common/ListContainer"; import PureListView from "../../common/PureListView"; import F8Colors from "../../common/F8Colors"; import StyleSheet from "../../common/F8StyleSheet"; import { Dimensions, View, Image, TouchableOpacity } from "react-native"; import F8Button from "../../common/F8Button"; import { Heading2, Heading5, Text } from "../../common/F8Text"; import F8BackgroundRepeat from "../../common/F8BackgroundRepeat"; import environment from "../../relay-environment"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, PADDING_HORIZONTAL = 12, ILLUSTRATION_HEIGHT = 505, ILLUSTRATION_VISIBLE = 36, ILLUSTRATION_OFFSET = ILLUSTRATION_HEIGHT - ILLUSTRATION_VISIBLE, PATTERN_VISIBLE = 12, PATTERN_HEIGHT = ILLUSTRATION_HEIGHT - 25, PATTERN_OFFSET = PATTERN_HEIGHT - PATTERN_VISIBLE; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {Array.} demos Parse Demo class * @param {Config} config Parse config vars * @param {F8Navigator} navigator Navigation methods * @return {ReactElement} * ============================================================================== */ class F8DemosView extends React.Component { constructor(props) { super(props); this.renderView = this.renderView.bind(this); this.onRowSelect = this.onRowSelect.bind(this); } render() { return ( { const sortedDemos = sortDemos(idx(props, _ => _.demos)); const demos = sortedDemos.filter(d => !d.devGarage); const garages = sortedDemos.filter(d => d.devGarage); const hasBookables = !!sortedDemos.find(d => d.booking); return ( this.props.navigator && this.props.navigator.push({ maps: true }) }} > {}} renderEmptyList={() => this.renderView(demos, garages, hasBookables)} /> ); }} /> ); } renderView(demos, garages, hasBookables) { const hasBothTables = demos.length && garages.length; return ( {"Here are the demos\nyou’ll find at F8."} {this.renderTable(demos, hasBothTables ? "Demos" : undefined)} {this.renderTable( this.props.garages, hasBothTables ? "Developer Garage" : undefined )} {this.renderManageReservationsButton(hasBookables)} ); } renderTable(rows: Array = [], groupTitle: ?string) { if (rows.length) { const tableHeading = groupTitle ? ( {groupTitle.toUpperCase()} ) : null; return ( {tableHeading} {rows.map((cell, index) => { const isFirst = index === 0; const { title, booking } = cell; let bookableFlag; if (booking) { bookableFlag = ( ); } const dividerStyles = isFirst ? null : styles.tableRowDivider; return ( {bookableFlag} this.onRowSelect(index, rows, groupTitle)} > {title} ); })} ); } else { return null; } } renderManageReservationsButton(hasBookables) { if (hasBookables && this.props.config.manageBookingsURL) { return ( this.props.navigator && this.props.navigator.push({ webview: this.props.config.manageBookingsURL, backgroundColor: F8Colors.turquoise, titleColor: F8Colors.white, itemsColor: F8Colors.white })} /> ); } else { return null; } } onRowSelect(selectedIndex: number, rows: Array, title: ?string) { this.props.navigator && this.props.navigator.push({ allDemos: rows, selectedIndex, title }); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: {}, contentContainer: { paddingTop: 11, paddingHorizontal: PADDING_HORIZONTAL, paddingBottom: 30 }, header: { alignItems: "center" }, headerBackground: { position: "absolute", left: 0, top: -PATTERN_OFFSET, right: 0 }, headerIllustration: { marginTop: -ILLUSTRATION_OFFSET }, mainHeading: { textAlign: "center", color: F8Colors.blue, marginTop: 10, marginBottom: 13 }, section: { marginTop: 10, marginBottom: 19 }, tableHeading: { textAlign: "center", color: F8Colors.pink }, table: { marginTop: 10, borderRadius: 2, borderWidth: 1, borderColor: "rgba(0,0,0,0.15)", backgroundColor: F8Colors.white }, tableRow: { height: 60 }, tableRowDivider: { borderTopWidth: 1, borderColor: F8Colors.colorWithAlpha("black", 0.15) }, tableRowLabel: { fontSize: 17, textAlign: "center", color: F8Colors.tangaroa, backgroundColor: "transparent", android: { paddingBottom: 5 } }, bookable: { position: "absolute", right: 0, top: 0, width: 86, height: 56 } }); /* redux select ============================================================= */ function sortDemos(demos = []) { const other = [], pinned = []; (demos || []).map(demo => { if (demo.booking) { pinned.push(demo); } else { other.push(demo); } }); return [...pinned, ...other]; } function select(store) { return { config: store.config }; } /* exports ================================================================== */ module.exports = connect(select)(F8DemosView); ================================================ FILE: js/tabs/demos/__generated__/F8DemosViewQuery.graphql.js ================================================ /** * @flow * @relayHash d1f7b51cfc6f9fe392e405fc2f8b3967 */ /* eslint-disable */ "use strict"; /*:: import type {ConcreteBatch} from 'relay-runtime'; export type F8DemosViewQueryResponse = {| +demos: ?$ReadOnlyArray; +logo: ?string; +logoHeight: ?number; +logoWidth: ?number; +devGarage: ?boolean; |}>; |}; */ /* query F8DemosViewQuery { demos { title description booking location links { title url } logo logoHeight logoWidth devGarage id } } */ const batch /*: ConcreteBatch*/ = { fragment: { argumentDefinitions: [], kind: "Fragment", metadata: null, name: "F8DemosViewQuery", selections: [ { kind: "LinkedField", alias: null, args: null, concreteType: "Demo", name: "demos", plural: true, selections: [ { kind: "ScalarField", alias: null, args: null, name: "title", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "description", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "booking", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "location", storageKey: null }, { kind: "LinkedField", alias: null, args: null, concreteType: "DemoLink", name: "links", plural: true, selections: [ { kind: "ScalarField", alias: null, args: null, name: "title", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "url", storageKey: null } ], storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logo", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logoHeight", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logoWidth", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "devGarage", storageKey: null } ], storageKey: null } ], type: "Query" }, id: null, kind: "Batch", metadata: {}, name: "F8DemosViewQuery", query: { argumentDefinitions: [], kind: "Root", name: "F8DemosViewQuery", operation: "query", selections: [ { kind: "LinkedField", alias: null, args: null, concreteType: "Demo", name: "demos", plural: true, selections: [ { kind: "ScalarField", alias: null, args: null, name: "title", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "description", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "booking", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "location", storageKey: null }, { kind: "LinkedField", alias: null, args: null, concreteType: "DemoLink", name: "links", plural: true, selections: [ { kind: "ScalarField", alias: null, args: null, name: "title", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "url", storageKey: null } ], storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logo", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logoHeight", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "logoWidth", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "devGarage", storageKey: null }, { kind: "ScalarField", alias: null, args: null, name: "id", storageKey: null } ], storageKey: null } ] }, text: "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" }; module.exports = batch; ================================================ FILE: js/tabs/info/AboutLocation.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View } from "react-native"; import { Paragraph, Heading4 } from "../../common/F8Text"; import F8Colors from "../../common/F8Colors"; import DirectionsLink from "./DirectionsLink"; type Props = { title: string, date: string, venue: string, address: string }; type State = {}; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {?string} title Section title * @param {?string} date Formatted date text * @param {?string} venue Venue name * @param {string} address Address used for opening maps * @return {ReactElement} * ============================================================================== */ export default class AboutLocation extends React.Component { props: Props; state: State = {}; render() { return ( {this.renderTitle()} {this.renderDate()} {this.renderVenue()} {this.renderAddress()} ); } renderTitle() { if (this.props.title) { return {this.props.title.toUpperCase()}; } else { return null; } } renderDate() { if (this.props.date) { return {this.props.date}; } else { return null; } } renderVenue() { if (this.props.venue) { return {this.props.venue}; } else { return null; } } renderAddress() { if (this.props.address) { return ( {this.props.address} ); } else { return null; } } } ================================================ FILE: js/tabs/info/CommonQuestions.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import F8Fonts from "../../common/F8Fonts"; import F8Linking from "../../common/F8Linking"; import { StyleSheet, View, Text } from "react-native"; import { Heading4, Paragraph } from "../../common/F8Text"; import Hyperlink from "react-native-hyperlink"; import F8Colors from "../../common/F8Colors"; class CommonQuestions extends React.Component { render() { let content = this.props.faqs.map(({ question, answer }) => ( )); return ( {this.renderTitle()} {content} ); } renderTitle() { if (this.props.title) { return {this.props.title.toUpperCase()}; } else { return null; } } } class Row extends React.Component { render() { return ( F8Linking.canOpenURL(url).then(supported => { if (supported) { F8Linking.openURL(url); } })} > {this.props.question} {this.props.answer} ); } } const styles = StyleSheet.create({ row: { marginVertical: 15 }, question: { fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold") }, hyperlink: { color: F8Colors.blue, textDecorationLine: "underline", textDecorationColor: F8Colors.blue } }); module.exports = CommonQuestions; ================================================ FILE: js/tabs/info/DirectionsLink.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { Platform, TouchableOpacity, ActionSheetIOS } from "react-native"; import { Text } from "../../common/F8Text"; import F8Linking from "../../common/F8Linking"; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {string} address Full address to open with selected maps app * @return {ReactElement} * ============================================================================== */ class DirectionsLink extends React.Component { static defaultProps = { address: "" }; render() { return ( {this.props.children} ); } onPress = _ => { if (Platform.OS === "ios") { ActionSheetIOS.showActionSheetWithOptions( { title: this.props.address, options: ["Open in Apple Maps", "Open in Google Maps", "Cancel"], destructiveButtonIndex: -1, cancelButtonIndex: 2 }, this.openMaps ); } else { // android let address = encodeURIComponent(this.props.address); F8Linking.openURL("https://maps.google.com/maps?&q=" + address); } }; openMaps = option => { const address = encodeURIComponent(this.props.address); switch (option) { case 0: F8Linking.openURL("https://maps.apple.com/?q=" + address); break; case 1: const nativeGoogleUrl = "comgooglemaps-x-callback://?q=" + address + "&x-success=f8://&x-source=F8"; F8Linking.canOpenURL(nativeGoogleUrl).then(supported => { const url = supported ? nativeGoogleUrl : "https://maps.google.com/?q=" + address; F8Linking.openURL(url); }); break; } }; } /* playground cards ========================================================= */ const directionsLink = DirectionsLink; directionsLink.__cards__ = define => { define("Large Blue/Green", _ => ( {"150 West San Carlos Street\nSan Jose, CA 95113"} )); }; /* exports ================================================================== */ module.exports = directionsLink; ================================================ FILE: js/tabs/info/F8AboutView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, Image, Dimensions } from "react-native"; import { HorizontalRule } from "../../common/F8Text"; import LinksList from "./LinksList"; import CommonQuestions from "./CommonQuestions"; import WiFiDetails from "./WiFiDetails"; import AboutLocation from "./AboutLocation"; /* constants ================================================================ */ const PADDING_HORIZONTAL = 18, WINDOW_WIDTH = Dimensions.get("window").width; /* props: navigator (for webview), pages, policies, faqs, config ============================================================================= */ class F8AboutView extends React.Component { constructor() { super(); this.webview = this.webview.bind(this); } webview(url) { this.props.navigator && this.props.navigator.push({ webview: url }); } render() { const imageW = WINDOW_WIDTH - PADDING_HORIZONTAL * 2; const imageH = imageW / (375 / 115); return ( {this.renderWiFiDetailsSection()} {this.renderFAQSection()} {this.renderPagesSection()} {this.renderPoliciesSection()} {this.renderThirdPartyNoticesSection()} ); } renderWiFiDetailsSection() { const { config } = this.props; if (config && config.wifiNetwork && config.wifiPassword) { return [ , ]; } else { return null; } } renderFAQSection() { const { faqs } = this.props; if (faqs && faqs.length) { return [ , ]; } else { return null; } } renderPagesSection() { const { pages } = this.props; if (pages && pages.length) { return [ , ]; } else { return null; } } renderPoliciesSection() { const { policies } = this.props; if (policies && policies.length) { return [ , ]; } else { return null; } } renderThirdPartyNoticesSection() { const { config } = this.props; const title = "Third Party Notices"; if (config && config.thirdPartyNotices) { return [ , ]; } else { return null; } } } /* export =================================================================== */ export default F8AboutView; ================================================ FILE: js/tabs/info/F8InfoView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { connect } from "react-redux"; import ListContainer from "../../common/ListContainer"; import PureListView from "../../common/PureListView"; import F8AboutView from "./F8AboutView"; import F8Colors from "../../common/F8Colors"; import F8NotificationsView from "../notifications/F8NotificationsView"; import { Platform, ActionSheetIOS } from "react-native"; import unseenNotificationsCount from "../notifications/unseenNotificationsCount"; import { testMenuEnabled, version } from "../../env"; import { TEST_MENU } from "../../actions"; /* ============================================================================= */ class F8InfoView extends React.Component { render() { return ( this.props.navigator && this.props.navigator.push({ maps: true }) }} {...this.renderTestItems()} > {}} renderEmptyList={_ => ( )} /> ); } renderTestItems() { if (!testMenuEnabled) { return {}; } if (Platform.OS === "ios") { return { rightItem: { title: "Test", onPress: () => this.showTestMenu() } }; } if (Platform.OS === "android") { return { extraItems: Object.keys(TEST_MENU).map(title => ({ title, onPress: () => this.props.dispatch(TEST_MENU[title]()) })) }; } } showTestMenu() { const itemTitles = Object.keys(TEST_MENU); ActionSheetIOS.showActionSheetWithOptions( { title: "Testing F8 app v" + version, options: ["Cancel", ...itemTitles], cancelButtonIndex: 0 }, idx => { if (idx === 0) { return; } const action: any = TEST_MENU[itemTitles[idx - 1]]; this.props.dispatch(action()); } ); } } /* redux store ============================================================== */ function select(store) { return { config: store.config, faqs: store.faqs, pages: store.pages, policies: store.policies, notificationsBadge: unseenNotificationsCount(store) + store.surveys.length }; } /* exports ================================================================== */ module.exports = connect(select)(F8InfoView); ================================================ FILE: js/tabs/info/LinksList.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import F8Touchable from "../../common/F8Touchable"; import F8Linking from "../../common/F8Linking"; import { View, Image, StyleSheet } from "react-native"; import { Text, Heading4 } from "../../common/F8Text"; /* constants ============================================================================= */ const ICON_SIZE = 45; /* ============================================================================= */ class LinksList extends React.Component { props: { title: string, links: Array<{ logo?: ?string, title: string, url?: string, onPress?: () => void }> }; constructor() { super(); this.onSelectRow = this.onSelectRow.bind(this); } onSelectRow(url, title) { this.props.onSelect && this.props.onSelect(url, title); } render() { let content = this.props.links.map(link => ( )); return ( {this.renderTitle()} {content} ); } renderTitle() { if (this.props.title) { return ( {this.props.title.toUpperCase()} ); } else { return null; } } } class Row extends React.Component { props: { link: { logo: ?string, title: string, url?: string, onPress?: () => void } }; render() { const { logo, title } = this.props.link; const image = logo && ( ); const rowHeight = image ? styles.tallRow : undefined; return ( {image} {title} ); } handlePress() { const { onSelect, link } = this.props; const { url, title } = link; // open in embedded web view if (onSelect) { onSelect(url, title); } else if (url) { F8Linking.openURL(url); } } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ heading: { paddingHorizontal: 18, marginBottom: 20 }, row: { flexDirection: "row", alignItems: "center", // paddingVertical: 8, paddingRight: 16, marginLeft: 12, height: 54 }, tallRow: { height: 62 // could be 63 }, rowIcon: { width: ICON_SIZE, height: ICON_SIZE, marginRight: 15 }, rowTitle: { paddingLeft: 6, color: F8Colors.tangaroa, fontSize: 17, flex: 1 }, button: { padding: 10 }, like: { // letterSpacing: 1, // color: F8Colors.actionText, // fontSize: 12, } }); /* Exports ============================================================================= */ module.exports = LinksList; ================================================ FILE: js/tabs/info/Section.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { StyleSheet, View } from "react-native"; import { Text } from "../../common/F8Text"; class Section extends React.Component { props: { title: string, children?: any, style?: any }; render() { return ( {this.props.title} {this.props.children} ); } } const styles = StyleSheet.create({ container: { paddingTop: 60, paddingBottom: 0, backgroundColor: "white" }, header: { flexDirection: "row", alignItems: "center", justifyContent: "center", marginBottom: 30 }, title: { fontSize: 24, fontWeight: "bold" } }); module.exports = Section; ================================================ FILE: js/tabs/info/WiFiDetails.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View } from "react-native"; import { Paragraph, Heading4 } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; /* Config ============================================================================= */ type Props = { network: string, password: string }; type State = {}; /* ============================================================================= ============================================================================= */ class WiFiDetails extends React.Component { props: Props; state: State = {}; render() { const { network, password } = this.props; return ( ); } } class Column extends React.Component { props: { label: string, value: string // style }; render() { return ( {this.props.label.toUpperCase()} {this.props.value} ); } } /* Styles ============================================================================= */ const styles = StyleSheet.create({ container: { paddingTop: 0, flexDirection: "row" }, colNetwork: { paddingHorizontal: 18 }, colPassword: { paddingHorizontal: 18 } }); /* Export ============================================================================= */ module.exports = WiFiDetails; ================================================ FILE: js/tabs/maps/F8MapView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { connect } from "react-redux"; import StyleSheet from "../../common/F8StyleSheet"; import { Dimensions, View } from "react-native"; import F8Colors from "../../common/F8Colors"; import F8Button from "../../common/F8Button"; import ListContainer from "../../common/ListContainer"; import MapView from "../../common/MapView"; import ActionsOverlay from "../../common/ActionsOverlay"; // static height calculations import F8Header from "../../common/F8Header"; const HEADER_HEIGHT = F8Header.height, WINDOW_WIDTH = Dimensions.get("window").width, WINDOW_HEIGHT = Dimensions.get("window").height, CONTROLS_HEIGHT = 90, MAP_HEIGHT = WINDOW_HEIGHT - HEADER_HEIGHT; class F8MapView extends React.Component { constructor(props) { super(props); this.state = { map: props.map1, width: WINDOW_WIDTH, height: MAP_HEIGHT }; } render() { const { map } = this.state; const { map1, map2 } = this.props; return ( this.switchMap(map1.name)} /> this.switchMap(map2.name)} /> this.props.navigator && this.props.navigator.pop()} /> ); } switchMap(mapName) { const { map1, map2 } = this.props; const { map } = this.state; if (mapName === map1.name && map.name !== map1.name) { this.setState({ map: map1 }); } else if (mapName === map2.name && map.name !== map2.name) { this.setState({ map: map2 }); } } } const styles = StyleSheet.create({ container: { flex: 1 }, nav: { position: "absolute", height: CONTROLS_HEIGHT, left: 0, bottom: 0, right: 0 }, navBtnContainer: { height: 64, alignSelf: "stretch", paddingTop: 8, paddingLeft: 14, paddingRight: 52 + 14, justifyContent: "center" } }); function select(store) { return { map1: store.maps.find(map => map.name === "Street Level"), map2: store.maps.find(map => map.name === "Upper Level") }; } module.exports = connect(select)(F8MapView); ================================================ FILE: js/tabs/maps/F8VenueMap.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { ScrollView, Image, Dimensions, Platform, PixelRatio } from "react-native"; import PhotoView from "react-native-photo-view"; import StyleSheet from "../../common/F8StyleSheet"; import F8Colors from "../../common/F8Colors"; /* Config ============================================================================= */ const SCREEN_WIDTH = Dimensions.get("window").width, SCREEN_HEIGHT = Dimensions.get("window").height; /* ============================================================================= ============================================================================= */ class F8VenueMap extends React.Component { static defaultProps = { width: SCREEN_WIDTH, height: SCREEN_HEIGHT, maximumZoomScale: 2 }; render() { const { map } = this.props; if (!map) { return null; } const url = urlForMap(map); const { width, height } = map; return Platform.OS === "ios" ? this.renderIOS(url, width, height) : this.renderAndroid(url); } renderAndroid(url) { return ( ); } renderIOS(url, width, height) { const contentHeight = this.props.height, contentWidth = contentHeight * (width / height); // minScale = Math.min(SCREEN_WIDTH/contentWidth, 1); return ( ); } } function urlForMap(map) { if (!map) { return ""; } switch (PixelRatio.get()) { case 1: return map.x1url; case 2: return map.x2url; case 3: return map.x3url; } return map.x3url; } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { backgroundColor: F8Colors.bianca } }); /* Export ============================================================================= */ module.exports = F8VenueMap; ================================================ FILE: js/tabs/maps/ZoomableImage.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import Image from "Image"; import React from "react"; import ScrollView from "ScrollView"; import { StyleSheet } from "react-native"; import TouchableWithoutFeedback from "TouchableWithoutFeedback"; class ZoomableImage extends React.Component { props: { url: string }; state: { lastTapTimestamp: number, isZoomed: boolean }; constructor() { super(); this.state = { lastTapTimestamp: 0, isZoomed: false }; (this: any).onZoomChanged = this.onZoomChanged.bind(this); (this: any).toggleZoom = this.toggleZoom.bind(this); } render() { return ( (this._zoomableScroll = c)} onScroll={this.onZoomChanged} scrollEventThrottle={100} scrollsToTop={false} alwaysBounceVertical={false} alwaysBounceHorizontal={false} automaticallyAdjustContentInsets={false} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} maximumZoomScale={4} centerContent={true} contentContainerStyle={{ flex: 1 }} > ); } toggleZoom(e: any) { const timestamp = new Date().getTime(); if (timestamp - this.state.lastTapTimestamp <= 500) { const { locationX, locationY } = e.nativeEvent; const size = this.state.isZoomed ? { width: 10000, height: 10000 } : { width: 0, height: 0 }; this._zoomableScroll.scrollResponderZoomTo({ x: locationX, y: locationY, ...size }); } this.setState({ lastTapTimestamp: timestamp }); } onZoomChanged(e: any) { this.setState({ isZoomed: e.nativeEvent.zoomScale > 1 }); } } const styles = StyleSheet.create({ image: { flex: 1, resizeMode: Image.resizeMode.contain } }); module.exports = ZoomableImage; ================================================ FILE: js/tabs/notifications/F8NotificationsView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import EmptySchedule from "../schedule/EmptySchedule"; import PushNUXModal from "./PushNUXModal"; import PureListView from "../../common/PureListView"; import React from "react"; import NotificationCell from "./NotificationCell"; import allNotifications from "./allNotifications"; import findSessionByURI from "./findSessionByURI"; import { connect } from "react-redux"; import { turnOnPushNotifications, skipPushNotifications } from "../../actions"; import { createSelector } from "reselect"; import { View } from "react-native"; import F8TimelineBackground from "../../common/F8TimelineBackground"; import F8Linking from "../../common/F8Linking"; /* ============================================================================= */ class F8NotificationsView extends React.Component { constructor(props) { super(props); (this: any).renderRow = this.renderRow.bind(this); (this: any).renderEmptyList = this.renderEmptyList.bind(this); (this: any).openNotification = this.openNotification.bind(this); (this: any).openReview = this.openReview.bind(this); } render() { return ( } showsVerticalScrollIndicator={false} /> ); } renderRow(notification, sid, rid) { return ( this.openNotification(notification)} firstRow={rid === 0 || rid === "0"} /> ); } renderEmptyList(containerHeight) { if (containerHeight === 0) { return null; } return ( ); } openNotification(notification) { if (notification.url) { const session = findSessionByURI(this.props.sessions, notification.url); if (session) { this.props.navigator.push({ session }); } else { F8Linking.openURL(notification.url); // this.props.navigator.push({ webview: notification.url }); // uses default theme } } else if (notification.survey) { this.props.navigator.push({ rate: 1, survey: notification.survey }); } } openReview() { this.props.navigator.push({ rate: 1, surveys: this.props.surveys }); } } /* redux ==================================================================== */ const data = createSelector( allNotifications, store => store.surveys, store => store.notifications.enabled, store => store.sessions, (notifications, surveys, enabled, sessions) => { const updatedSurveys = surveys.map(survey => { const surveySession = sessions.find(s => s.id === survey.sessionId); return { text: `How was "${surveySession.title}"?`, time: survey.time, survey }; }); return [...updatedSurveys, ...notifications].sort(function(a, b) { return b.time - a.time; }); } ); function select(state) { return { nux: state.notifications.enabled === null, notifications: data(state), sessions: state.sessions }; } function actions(dispatch) { return { onTurnOnNotifications: () => dispatch(turnOnPushNotifications()), onSkipNotifications: () => dispatch(skipPushNotifications()), dispatch }; } /* exports ================================================================== */ module.exports = connect(select, actions)(F8NotificationsView); ================================================ FILE: js/tabs/notifications/NotificationCell.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Colors from "../../common/F8Colors"; import F8SessionCell from "../schedule/F8SessionCell"; import React from "react"; import findSessionByURI from "./findSessionByURI"; import TouchableHighlight from "../../common/F8Touchable"; import moment from "moment"; import { connect } from "react-redux"; import { Dimensions, View, Image } from "react-native"; import { Text, Paragraph, Heading3 } from "../../common/F8Text"; import F8TimelineSegment from "../../common/F8TimelineSegment"; import F8Fonts from "../../common/F8Fonts"; import StyleSheet from "../../common/F8StyleSheet"; import F8Button from "../../common/F8Button"; /* constants ================================================================ */ const IMAGE_RATIO = 230 / 375, CELL_PADDING_VERTICAL = 14, CELL_PADDING_LEFT = 50, CELL_PADDING_RIGHT = 34, TIMELINE_LEFT = 24, TIMELINE_DOT_OFFSET_TOP = CELL_PADDING_VERTICAL, // + 3? IMG_MARGIN_TOP = 18, SCREEN_WIDTH = Dimensions.get("window").width; const ICON_TYPES = { // matches names in getType() default: { defaultIcon: require("./img/timeline/triangle.png"), activeIcon: require("./img/timeline/triangle-active.png") }, session: { defaultIcon: require("./img/timeline/circle.png"), activeIcon: require("./img/timeline/circle-active.png") }, survey: { defaultIcon: require("./img/timeline/circle.png"), activeIcon: require("./img/timeline/circle-active.png") }, image: { defaultIcon: require("./img/timeline/square.png"), activeIcon: require("./img/timeline/square-active.png") }, link: { defaultIcon: require("./img/timeline/circle.png"), activeIcon: require("./img/timeline/circle-active.png") } }; /* ============================================================================= -------------------------------------------------------------------------------- Props: ! notification:object ! onPress:function ============================================================================= */ class NotificationCell extends React.Component { render() { const notificationType = this.getType(); const content = ( {this.renderTimelineSegment(notificationType)} {this.renderTimeAndText()} {this.renderAttachmentByType(notificationType)} ); return this.props.notification.url || this.props.notification.survey ? ( {content} ) : ( content ); } renderTimelineSegment(type: string) { const { defaultIcon, activeIcon } = ICON_TYPES[type]; const timelineTopOffset = this.props.firstRow ? TIMELINE_DOT_OFFSET_TOP : 0; return ( ); } renderTimeAndText() { const { notification } = this.props; return [ {moment(notification.time) .fromNow() .toUpperCase()} , {notification.text} ]; } renderAttachmentByType(type: string) { if (type === "image") { return this.renderImageAttachment(); } else if (type === "session") { return this.renderSessionAttachment(); } else if (type === "survey") { return this.renderSurveyAttachment(); } else if (type === "link") { return this.renderLinkAttachment(); } else { return null; } } renderImageAttachment() { const { notification } = this.props; const imageW = SCREEN_WIDTH; const imageH = imageW * IMAGE_RATIO; let imageCTAButton; if (notification.urlTitle) { const watchIcon = notification.urlTitle.toLowerCase().indexOf("watch") > -1 ? require("../../common/img/buttons/play-medium.png") : null; imageCTAButton = ( ); } return ( {imageCTAButton} ); } renderSessionAttachment() { return ( ); } renderSurveyAttachment() { return ( Leave a review ); } renderLinkAttachment() { const { notification } = this.props; const linkText = notification.url .replace("https://", "") .replace("http://", ""); const linkTitle = notification.urlTitle ? ( {notification.urlTitle} ) : null; return ( {linkTitle} {linkText} ); } getType() { const { notification, session } = this.props; if (session) { return "session"; } else if (notification.survey) { return "survey"; } else if (notification.image) { return "image"; } else if (notification.url) { return "link"; } else { return "default"; } } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ cell: { paddingVertical: CELL_PADDING_VERTICAL, paddingLeft: CELL_PADDING_LEFT, paddingRight: CELL_PADDING_RIGHT }, firstCell: { marginTop: 15 }, time: { fontFamily: F8Fonts.helvetica, color: F8Colors.colorWithAlpha("black", 0.5), fontSize: 13, marginBottom: 5 }, text: { lineHeight: 22 }, embeddedCard: { marginTop: 15, borderWidth: 1, borderRadius: 2, borderColor: F8Colors.tangaroa, backgroundColor: F8Colors.white }, embeddedSession: { paddingTop: 12, paddingBottom: 14 }, embeddedLink: { padding: 18 }, embeddedSurvey: { paddingVertical: 15, paddingHorizontal: 18, flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, url: { // flex: 1, color: F8Colors.blue, fontSize: 13, marginTop: 3 // marginBottom: 10, }, image: { marginTop: IMG_MARGIN_TOP, marginLeft: -CELL_PADDING_LEFT, alignItems: "center", justifyContent: "center" } }); /* data store =============================================================== */ function select(store, props) { return { session: findSessionByURI(store.sessions, props.notification.url), isSeen: store.notifications.seen[props.notification.id] }; } /* exports ================================================================== */ module.exports = connect(select)(NotificationCell); ================================================ FILE: js/tabs/notifications/PushNUXModal.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Button from "../../common/F8Button"; import F8Colors from "../../common/F8Colors"; import { Dimensions, View, Image, StyleSheet } from "react-native"; import { Heading2, Paragraph } from "../../common/F8Text"; import F8BackgroundRepeat from "../../common/F8BackgroundRepeat"; import F8Modal from "../../common/F8Modal"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, MODAL_PADDING_H = 10, MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2; /* ============================================================================= */ class PushNUXModal extends React.Component { props: { onTurnOnNotifications: () => void, onSkipNotifications: () => void }; render() { return ; } renderContent = _ => { return ( Dont miss out! Turn on push notifications to see what’s happening at F8. You can always see in-app updates on this tab. ); }; } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ headerBackground: { position: "absolute", left: 0, top: 0 }, headerIllustration: { alignSelf: "center" }, content: { paddingTop: 32, paddingHorizontal: 20, paddingBottom: 45, alignItems: "center" }, text: { textAlign: "center", paddingTop: 6, paddingHorizontal: 10, paddingBottom: 35, fontSize: 15, lineHeight: 24, color: F8Colors.colorWithAlpha("tangaroa", 0.7) }, button: { marginTop: 9, alignSelf: "stretch" } }); /* exports ================================================================== */ module.exports = PushNUXModal; ================================================ FILE: js/tabs/notifications/RateSessionsCell.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { StyleSheet, TouchableOpacity } from "react-native"; import F8Fonts from "../../common/F8Fonts"; import F8Colors from "../../common/F8Colors"; import { Text } from "../../common/F8Text"; type Props = { numberOfSessions: number, onPress: () => void }; /* ============================================================================= */ function RateSessionsCell({ numberOfSessions, onPress }: Props) { const label = `Rate the session${numberOfSessions === 1 ? "" : "s"} you attended`; return ( {label.toUpperCase()} ); } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ cell: { alignItems: "center", justifyContent: "center", padding: 15, backgroundColor: F8Colors.yellow }, text: { fontSize: 13, fontFamily: F8Fonts.helvetica, color: F8Colors.pink } }); /* exports ============================================================================= */ module.exports = RateSessionsCell; ================================================ FILE: js/tabs/notifications/allNotifications.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import { createSelector } from "reselect"; import type { Notification } from "../../reducers/notifications"; // Merges lists of notifications from server and notifications // received via push and makes sure there is no duplicates. function mergeAndSortByTime( server: Array, push: Array ): Array { const uniquePush = push.filter(pushNotification => { const existsOnServer = server.find( serverNotification => serverNotification.text === pushNotification.text ); return !existsOnServer; }); const all = [].concat(server, uniquePush); return all.sort((a, b) => b.time - a.time); } module.exports = createSelector( store => store.notifications.server, store => store.notifications.push, mergeAndSortByTime ); ================================================ FILE: js/tabs/notifications/findSessionByURI.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Session } from "../../reducers/sessions"; function findSessionByURI(sessions: Array, uri: ?string): ?Session { if (!uri) { return null; } const slug = uri.replace("f8://", ""); for (let i = 0; i < sessions.length; i++) { const session = sessions[i]; if (session.slug === slug || session.id === slug) { return session; } } return null; } module.exports = findSessionByURI; ================================================ FILE: js/tabs/notifications/unseenNotificationsCount.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import allNotifications from "./allNotifications"; import { createSelector } from "reselect"; import type { Notification, SeenNotifications } from "../../reducers/notifications"; function unseenNotificationsCount( notifications: Array, seen: SeenNotifications ): number { return notifications.filter(notification => !seen[notification.id]).length; } module.exports = createSelector( allNotifications, store => store.notifications.seen, unseenNotificationsCount ); ================================================ FILE: js/tabs/schedule/AddToScheduleButton.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { Animated } from "react-native"; import F8Button from "../../common/F8Button"; type Props = { isAdded: boolean, onPress: () => void, addedImageSource?: ?string, style?: any }; type State = { anim: Animated.Value }; const SAVED_LABEL = "Added to my F8"; const ADD_LABEL = "Add to my F8"; class AddToScheduleButton extends React.Component { props: Props; state: State; constructor(props: Props) { super(props); this.state = { anim: new Animated.Value(props.isAdded ? 1 : 0) }; } render() { const { isAdded, addedImageSource, style } = this.props; const buttonTheme = isAdded ? "yellow" : "pink"; const caption = isAdded ? SAVED_LABEL : ADD_LABEL; const icon = isAdded && addedImageSource ? addedImageSource : require("./img/added.png"); return ( ); } componentWillReceiveProps(nextProps: Props) { if (this.props.isAdded !== nextProps.isAdded) { const toValue = nextProps.isAdded ? 1 : 0; Animated.spring(this.state.anim, { toValue }).start(); } } } module.exports = AddToScheduleButton; // $FlowFixMe module.exports.__cards__ = define => { let f; setInterval(() => f && f(), 1000); define("Inactive", (state, update) => ( update(!state)} /> )); define("Active", (state, update) => ( update(!state)} /> )); define("Animated", (state = false, update) => { f = () => update(!state); return {}} />; }); }; ================================================ FILE: js/tabs/schedule/EmptySchedule.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { Paragraph, Heading3 } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; import { View, Image } from "react-native"; import F8Colors from "../../common/F8Colors"; class EmptySchedule extends React.Component { props: { style?: any, title?: string, titleStyles?: any, image?: number, text: string, textStyles?: any, children?: any }; render() { const image = this.props.image && ( ); const title = this.props.title && ( {this.props.title} ); return ( {image} {title} {this.props.text} {this.props.children} ); } } const styles = StyleSheet.create({ container: { justifyContent: "center" }, content: { padding: 30, alignItems: "center" }, image: { marginBottom: 20 }, title: { color: F8Colors.blue, textAlign: "center", marginBottom: 10 }, text: { textAlign: "center", marginBottom: 35 } }); module.exports = EmptySchedule; ================================================ FILE: js/tabs/schedule/F8FriendGoing.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import ProfilePicture from "../../common/ProfilePicture"; import React from "react"; import { Image, StyleSheet, View } from "react-native"; import { Text } from "../../common/F8Text"; import F8Touchable from "../../common/F8Touchable"; import type { FriendsSchedule } from "../../reducers/friendsSchedules"; class F8FriendGoing extends React.Component { props: { onPress: () => void, friend: FriendsSchedule }; render() { return ( {this.props.friend.name} ); } } const styles = StyleSheet.create({ container: { flexDirection: "row", alignItems: "center", paddingVertical: 10, backgroundColor: "white" }, name: { marginLeft: 12, fontSize: 17, flex: 1 } }); module.exports = F8FriendGoing; ================================================ FILE: js/tabs/schedule/F8GanttGrid.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * @flow */ "use strict"; import moment from "moment-timezone"; import React from "react"; import { View, StyleSheet } from "react-native"; import { Text } from "../../common/F8Text"; import { timezone } from "../../env.js"; /* constants ================================================================ */ const LABELS_WIDTH = 30, LABELS_HEIGHT = 22; /* ============================================================================= ============================================================================= */ export default class F8GanttGrid extends React.Component { render() { const ms = moment.tz(this.props.startTime, timezone), me = moment.tz(this.props.endTime, timezone); const diff = me.diff(ms, "hours"); const cols = this.renderColumns(ms, diff); return ( {cols} ); } renderColumns(momentStart: moment, count: number) { let cols = []; let previousLabelAMPM = null; for (let i = 0; i < count + 1; i++) { const left = this.props.containerWidth / count * i - LABELS_WIDTH / 2; let label = null; if (i % 2 === 0) { let labelText = momentStart.add(i, "h").format("h"); let ampm = momentStart .format("A") .split("M") .join(""); if (previousLabelAMPM && ampm !== previousLabelAMPM) { labelText += momentStart .format("A") .split("M") .join(""); } // if(ampm !== previousLabelAMPM) labelText += (momentStart.format('A')).split('M').join(''); previousLabelAMPM = momentStart .format("A") .split("M") .join(""); label = {labelText}; momentStart.subtract(i, "h"); } cols.push( {label} ); } return cols; } } /* Styles ============================================================================= */ const styles = StyleSheet.create({ gridContainer: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0 }, gridColumn: { position: "absolute", left: 0, top: 0, bottom: 0, width: LABELS_WIDTH, backgroundColor: "transparent" //'#2d3132', // alignItems: 'center', }, gridLine: { position: "absolute", left: LABELS_WIDTH / 2, top: 0, bottom: LABELS_HEIGHT, width: 1, backgroundColor: "rgba(22, 51, 96, 1)" }, gridColumnLabel: { position: "absolute", paddingTop: 6, left: 0, right: 0, height: LABELS_HEIGHT, bottom: 0, //-LABELS_HEIGHT, textAlign: "center", fontSize: 10, color: "rgba(95, 118, 162, 1)", backgroundColor: "transparent" } }); ================================================ FILE: js/tabs/schedule/F8GanttNowMarker.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import moment from "moment-timezone"; import React from "react"; import { View, StyleSheet } from "react-native"; import F8Colors from "../../common/F8Colors"; /* constants ================================================================ */ const NOW_MARKER_DOT = 6, NOW_MARKER_LINE = 1, LABELS_HEIGHT = 22; /* ============================================================================= ============================================================================= */ export default class F8GanttNowMarker extends React.Component { render() { const { nowTime, startTime, endTime, containerWidth } = this.props; const mNow = moment.utc(nowTime), mDayStart = moment.utc(startTime), mDayEnd = moment.utc(endTime); const minutesTotalDayLength = mDayEnd.diff(mDayStart, "minutes"), minutesSinceStartOfDay = mNow.diff(mDayStart, "minutes"); const pos = containerWidth / minutesTotalDayLength * minutesSinceStartOfDay - NOW_MARKER_DOT / 2; return ( ); } } /* Styles ============================================================================= */ const styles = StyleSheet.create({ container: { position: "absolute", left: 0, top: 0, bottom: LABELS_HEIGHT, width: NOW_MARKER_DOT, backgroundColor: "transparent" }, line: { position: "absolute", width: NOW_MARKER_LINE, left: NOW_MARKER_DOT / 2 - NOW_MARKER_LINE / 2, top: 0, bottom: NOW_MARKER_DOT / 2, backgroundColor: F8Colors.white }, dot: { position: "absolute", width: NOW_MARKER_DOT, height: NOW_MARKER_DOT, borderRadius: NOW_MARKER_DOT / 2, bottom: 0, left: 0, backgroundColor: F8Colors.white } }); ================================================ FILE: js/tabs/schedule/F8GanttRow.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import moment from "moment-timezone"; import React from "react"; import { View, StyleSheet, Image } from "react-native"; import { Text } from "../../common/F8Text"; import F8Colors from "../../common/F8Colors"; /* constants ================================================================ */ const ROW_HEIGHT = 26; /* ============================================================================= ============================================================================= */ export default class F8GanttRow extends React.Component { calculateSize() { const { sessionStart, sessionEnd, dayStart, dayEnd, containerWidth } = this.props; const mSessionStart = moment.utc(sessionStart), mSessionEnd = moment.utc(sessionEnd), mDayStart = moment.utc(dayStart), mDayEnd = moment.utc(dayEnd); const sessionLength = mSessionEnd.diff(mSessionStart, "minutes"), dayLength = mDayEnd.diff(mDayStart, "minutes"), daySessionStartDiff = mSessionStart.diff(mDayStart, "minutes"); return { left: daySessionStartDiff / dayLength * containerWidth, width: sessionLength / dayLength * containerWidth }; } render() { const { location, title, offset } = this.props; const { left, width } = this.calculateSize(); let tintColor = location ? F8Colors.colorForLocation(location.toUpperCase()) : F8Colors.blue; if (location.toUpperCase().indexOf("REGISTRATION") > -1) { tintColor = F8Colors.yellow; } return ( {title} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { position: "absolute", left: 0, right: 0, height: ROW_HEIGHT, backgroundColor: "transparent" }, label: { paddingLeft: 2, fontSize: 13, letterSpacing: -0.1, color: F8Colors.white }, barContainer: { position: "absolute", left: 0, bottom: 0, height: 6 }, bar: { position: "absolute", left: 0, right: 0, top: 2.5, height: 1, overflow: "hidden", backgroundColor: F8Colors.colorWithAlpha("iceberg", 0.6) }, barStartIcon: { position: "absolute", left: 0, bottom: 0 }, barEndIcon: { position: "absolute", right: 9, bottom: 0 }, barColorIcon: { position: "absolute", right: 0, bottom: 0 } }); ================================================ FILE: js/tabs/schedule/F8ScheduleGantt.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import moment from "moment-timezone"; import React from "react"; import { View, StyleSheet } from "react-native"; import F8GanttGrid from "./F8GanttGrid"; import F8GanttNowMarker from "./F8GanttNowMarker"; import F8GanttRow from "./F8GanttRow"; /* constants ================================================================ */ const ROW_HEIGHT = 26, ROW_GUTTERS = 10, GRID_PADDING = 10, LABELS_HEIGHT = 22; /* ============================================================================= -------------------------------------------------------------------------------- Props: ! sessions:array ! now:number ============================================================================= */ export default class F8ScheduleGantt extends React.Component { constructor(props) { super(props); const { filtered, earliest, latest } = this.filterSessions(props.sessions); this.state = { now: props.now, filteredSessions: filtered, dayStart: earliest, dayEnd: latest }; } componentWillReceiveProps(nextProps) { const newState = {}; if (nextProps.now !== this.props.now) { newState.now = nextProps.now; } if (nextProps.sessions !== this.props.sessions) { const { filtered, earliest, latest } = this.filterSessions( nextProps.sessions ); newState.filteredSessions = filtered; newState.dayStart = earliest; newState.dayEnd = latest; } if (Object.keys(newState).length) { this.setState({ ...newState }); } } shouldComponentUpdate(nextProps, nextState) { return nextProps !== this.props || nextState !== this.state; } filterSessions(all) { let grouped = this.groupSessionsIntoRows(all); let filtered = []; let earliest = null, latest = null; (Object.keys(grouped) || []).map(title => { let session = { title, startTime: null, endTime: null, times: [] }; (grouped[title] || []).map(each => { // find the earliest and latest start/end times if (!earliest || each.startTime < earliest) { earliest = each.startTime; } if (!latest || each.endTime > latest) { latest = each.endTime; } // update grouped session start/end times (if necessary) if (!session.startTime || each.startTime < session.startTime) { session.startTime = each.startTime; } if (!session.endTime || each.endTime > session.endTime) { session.endTime = each.endTime; } // pass through multiple times, deprecated if (title !== "Sessions" && each.times && each.times.length) { session.times = [...session.times, ...each.times]; } // set the location (if necessary) if (!session.location) { session.location = each.location; } }); filtered.push(session); }); const roundedDownStart = moment .utc(earliest) .startOf("hour") .valueOf(), endMoment = moment.utc(latest), roundedUpEnd = endMoment.minute() || endMoment.second() || endMoment.millisecond() ? endMoment.add(1, "hour").startOf("hour") : endMoment.startOf("hour"); return { filtered, earliest: roundedDownStart, latest: roundedUpEnd }; } groupSessionsIntoRows(all) { let grouped = {}; (all || []).map(session => { if (session.day !== this.props.day) { return; } if (!session.hasDetails && !grouped[session.title]) { grouped[session.title] = [session]; // start sessions array as its a new entry } else if (!session.hasDetails && grouped[session.title]) { grouped[session.title].push(session); // start sessions array as its a new entry } else if ( session.hasDetails && session.title.indexOf("Keynote") > -1 && !grouped[session.title] ) { grouped[session.title] = [session]; } else if (session.hasDetails && !grouped.Sessions) { grouped.Sessions = [session]; } else if (session.hasDetails && grouped.Sessions) { grouped.Sessions.push(session); } }); return grouped; } render() { const { filteredSessions } = this.state; if (filteredSessions.length < 1) { return null; } const rows = filteredSessions.map((session, index) => this.renderRow(session, index) ); const gutters = rows.length > 1 ? ROW_GUTTERS * (rows.length - 1) : 0; const height = ROW_HEIGHT * rows.length + gutters + GRID_PADDING * 2 + LABELS_HEIGHT; const grid = this.renderGrid( height, this.state.dayStart, this.state.dayEnd ); let now; if ( this.state.now >= this.state.dayStart && this.state.now <= this.state.dayEnd ) { now = this.renderNow( this.state.now, this.state.dayStart, this.state.dayEnd ); } return ( {grid} {rows} {now} ); } renderRow(session, i) { const offset = ROW_HEIGHT * i + ROW_GUTTERS * i; return ( ); } renderGrid(height, start, end) { return ( ); } renderNow(now, start, end) { return ( ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: {} }); ================================================ FILE: js/tabs/schedule/F8SessionCell.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import formatDuration from "./formatDuration"; import formatTime from "./formatTime"; import { connect } from "react-redux"; import type { Session } from "../../reducers/sessions"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import { Text } from "../../common/F8Text"; import F8TimelineSegment from "../../common/F8TimelineSegment"; import { TouchableOpacity, View, Image, StyleSheet } from "react-native"; /* Constants ============================================================================= */ const CELL_PADDING_TOP = 8, CELL_PADDING_RIGHT = 20, CELL_PADDING_BOTTOM = 12, DURATION_FONT_SIZE = 14, CELL_LEFT = 95, TIMELINE_LEFT = CELL_LEFT - 18, TIMELINE_DOT_TOP = CELL_PADDING_TOP + 7; /* ============================================================================= ============================================================================= */ class F8SessionCell extends React.Component { props: { session: Session, isFavorite: boolean, showStartEndTime: boolean, onPress: ?() => void, style: any }; static defaultProps = { firstRow: false, embedded: false }; render() { const { embedded, isFavorite } = this.props; const embeddedStyles = embedded ? styles.cellEmbedded : null; return ( {!embedded ? this.renderTimeline() : null} {this.renderContent()} {isFavorite ? this.renderFavoritesIcon() : null} ); } renderTimeline() { const { firstRow } = this.props; if (firstRow) { return ( ); } else { return ( ); } } renderContent() { if (this.props.onPress) { return ( {this.renderTitle()} {this.renderMeta()} ); } else { return [this.renderTitle(), this.renderMeta()]; } } renderTitle() { const { session } = this.props; const embedded = this.props.embedded ? styles.titleEmbedded : null; return ( {session.title} ); } renderMeta() { const { session } = this.props; return ( {session.location.toUpperCase()} {" - "} {this.getFormattedTime()} ); } renderFavoritesIcon() { const { title } = this.props.session; let iconSource = require("./img/added.png"); if (title && title.toLowerCase().indexOf("react") > -1) { iconSource = require("./img/added-react.png"); } return ( ); } getFormattedTime() { const { startTime, endTime } = this.props.session; if (this.props.showStartEndTime) { return formatTime(startTime, true) + "-" + formatTime(endTime); } else { return formatDuration(startTime, endTime); } } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ cell: { paddingTop: CELL_PADDING_TOP, paddingBottom: CELL_PADDING_BOTTOM, paddingLeft: CELL_LEFT, paddingRight: CELL_PADDING_RIGHT, // backgroundColor: F8Colors.background, justifyContent: "center" }, cellEmbedded: { paddingLeft: CELL_PADDING_RIGHT }, titleSection: { paddingRight: 9, flexDirection: "row", alignItems: "center" }, titleAndDuration: { justifyContent: "center" }, titleText: { flex: 1, fontSize: F8Fonts.normalize(17), lineHeight: 22, color: F8Colors.tangaroa, marginBottom: 3, marginRight: 10 }, titleEmbedded: { fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold") }, duration: { fontSize: DURATION_FONT_SIZE, color: F8Colors.colorWithAlpha("tangaroa", 0.6) }, added: { position: "absolute", backgroundColor: F8Colors.yellow, alignItems: "center", justifyContent: "center", width: 23, height: 21, right: 0, top: CELL_PADDING_TOP } }); /* Redux ============================================================================= */ function select(store, props) { return { isFavorite: !!store.schedule[props.session.id] }; } /* Export ============================================================================= */ module.exports = connect(select)(F8SessionCell); ================================================ FILE: js/tabs/schedule/F8SessionDetails.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Colors from "../../common/F8Colors"; import F8FriendGoing from "./F8FriendGoing"; import F8SpeakerProfile from "./F8SpeakerProfile"; import MapView from "../../common/MapView"; import React from "react"; import AddToScheduleButton from "./AddToScheduleButton"; import formatDuration from "./formatDuration"; import { connect } from "react-redux"; import { addToSchedule, removeFromScheduleWithPrompt } from "../../actions"; import { Dimensions, View, ScrollView, PixelRatio } from "react-native"; import { Text, Heading2, Heading4, Paragraph } from "../../common/F8Text"; import F8Fonts from "../../common/F8Fonts"; import ActionsOverlay from "../../common/ActionsOverlay"; import F8ScrollingHeader from "../../common/F8ScrollingHeader"; import StyleSheet from "../../common/F8StyleSheet"; import SharingSettingsModal from "./SharingSettingsModal"; import LoginModal from "../../login/LoginModal"; import Carousel from "../../common/Carousel"; const WINDOW_WIDTH = Dimensions.get("window").width, HORIZONTAL_BREAKPOINT = WINDOW_WIDTH <= 320, CONTENT_PADDING_H = HORIZONTAL_BREAKPOINT ? 20 : 30; const F8SessionDetails = React.createClass({ getInitialState: function() { return { scrollTop: 0, sharingModal: false, loginModal: false }; }, render: function() { return ( this.setState({ scrollTop: nativeEvent.contentOffset.y })} scrollEventThrottle={100} showsVerticalScrollIndicator={false} automaticallyAdjustContentInsets={false} > {this.renderLocation()} {this.renderTitle()} {this.renderDescription()} {this.renderSpeakers()} {this.renderFriendsGoing()} {this.renderMap()} {this.renderActions()} {this.renderScrollingHeader()} {this.renderModals()} ); }, renderLocation() { const { session } = this.props; const locationColor = F8Colors.colorForLocation(session.location); const locationTitle = session.location && session.location.toUpperCase(); return ( {locationTitle} {locationTitle && " - "} {formatDuration(session.startTime, session.endTime).toUpperCase()} ); }, renderTitle() { if (this.props.session.title) { return ( {this.props.session.title} ); } else { return null; } }, renderDescription() { if (this.props.session.description) { return {this.props.session.description}; } else { return null; } }, renderSpeakers() { const speakersProfiles = (this.props.session.speakers || [] ).map(speaker => ( )); if (speakersProfiles.length) { return
{speakersProfiles}
; } else { return null; } }, renderFriendsGoing() { const friendsGoing = this.props.friendsGoing.map(friend => ( this.props.navigator.push({ friend })} /> )); if (friendsGoing.length) { return
{friendsGoing}
; } else { return null; } }, renderMap() { if (!this.props.map) { return null; } const mapWidth = Carousel.CardWidth - CONTENT_PADDING_H * 2; return ; }, renderActions() { const title = this.props.session.title || ""; const isReactTalk = title.indexOf("React") > -1; return ( ); }, renderScrollingHeader() { const { title } = this.props.session; return ( ); }, renderModals() { return [ { this.setState({ sharingModal: false }); }} />, this.setState({ loginModal: false })} /> ]; }, toggleAdded: function() { if (this.props.isAddedToSchedule) { this.props.removeFromScheduleWithPrompt(); } else { this.addToSchedule(); } }, addToSchedule: function() { if (!this.props.isLoggedIn) { // this.props.navigator.push({ // login: true, // TODO: Proper route // callback: this.addToSchedule, // }); this.setState({ loginModal: true }); } else { this.props.addToSchedule(); if (this.props.sharedSchedule === null) { setTimeout(_ => this.setState({ sharingModal: true }), 1000); // setTimeout(() => this.props.navigator.push({share: true}), 1000); } } } }); class Section extends React.Component { props: { title?: string, children?: any }; render() { const { children } = this.props; if (React.Children.count(children) === 0) { return null; } let header; if (this.props.title) { header = ( {this.props.title.toUpperCase()} ); } return ( {header} {children} ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "white" }, contentContainer: { paddingTop: 23, paddingHorizontal: CONTENT_PADDING_H, paddingBottom: 93 }, miniHeader: { backgroundColor: F8Colors.white, flexDirection: "row", alignItems: "center", justifyContent: "center", position: "absolute", left: 24, top: 0, right: 24, paddingVertical: 9, borderBottomWidth: 1 / PixelRatio.get(), borderBottomColor: "rgba(153, 162, 178, 1)" }, miniTitle: { fontSize: 13, color: F8Colors.tangaroa, ios: { textAlign: "left" } }, location: { fontSize: 13, fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "helveticaBold") }, time: { color: F8Colors.tangaroa, fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "helveticaBold") }, title: { marginVertical: 15, color: F8Colors.blue }, section: { marginTop: 20 }, sectionTitle: { marginBottom: 6 }, actions: { position: "absolute", left: 0, right: 0, bottom: 0 }, map: { marginTop: 32 } }); function select(store, props) { const sessionID = props.session.id; const friendsGoing = store.friendsSchedules.filter( friend => friend.schedule && friend.schedule[sessionID] ); const map = store.maps.find(({ name }) => name === props.session.location); return { isAddedToSchedule: !!store.schedule[props.session.id], isLoggedIn: store.user.isLoggedIn, sharedSchedule: store.user.sharedSchedule, sessionURLTemplate: store.config.sessionURLTemplate, topics: store.topics, friendsGoing, map }; } function actions(dispatch, props) { let id = props.session.id; return { addToSchedule: () => dispatch(addToSchedule(id)), removeFromScheduleWithPrompt: () => dispatch(removeFromScheduleWithPrompt(props.session)) }; } module.exports = connect(select, actions)(F8SessionDetails); ================================================ FILE: js/tabs/schedule/F8SpeakerProfile.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Colors from "../../common/F8Colors"; import React from "react"; import { Heading3, Text } from "../../common/F8Text"; import { StyleSheet, View } from "react-native"; const F8SpeakerProfile = React.createClass({ render: function() { const speaker = this.props.speaker; return ( {speaker.name} {speaker.title ? ( {speaker.title} ) : null} ); } }); const styles = StyleSheet.create({ row: { paddingBottom: 14 }, name: { color: F8Colors.blue }, title: { fontSize: 13, lineHeight: 16, color: F8Colors.tangaroa } }); module.exports = F8SpeakerProfile; ================================================ FILE: js/tabs/schedule/FilterHeader.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { View, Image, TouchableOpacity } from "react-native"; import { Text } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; import F8Colors from "../../common/F8Colors"; /* constants ================================================================ */ const CONTAINER_HEIGHT = 39; /* ============================================================================= */ class FilterHeader extends React.Component { static defaultProps = { backgroundColor: F8Colors.tangaroa, textColor: F8Colors.white }; render() { const topics = Object.keys(this.props.filter); if (topics.length === 0) { return null; } const { backgroundColor, textColor } = this.props; return ( {`Filter${topics.length > 1 ? "s" : ""}: ${topics.join(", ")}`} ); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { height: CONTAINER_HEIGHT, flexDirection: "row", alignItems: "center", ios: { paddingLeft: 37 }, android: { paddingLeft: 12 } }, text: { flex: 1, fontSize: 12, ios: { textAlign: "center" } }, clear: { paddingHorizontal: 12, alignSelf: "stretch", justifyContent: "center" } }); /* exports ================================================================== */ module.exports = FilterHeader; ================================================ FILE: js/tabs/schedule/FriendCell.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import { View, Image, StyleSheet, TouchableHighlight } from "react-native"; import { Text } from "../../common/F8Text"; import ProfilePicture from "../../common/ProfilePicture"; import type { FriendsSchedule } from "../../reducers/friendsSchedules"; class FriendCell extends React.Component { props: { friend: FriendsSchedule, onPress: ?() => void }; render() { const { friend } = this.props; const hasSchedule = friend.schedule && Object.keys(friend.schedule).length > 0; const auxView = hasSchedule ? ( ) : ( ); const cell = ( {friend.name} {auxView} ); if (!hasSchedule) { return cell; } else { return ( {cell} ); } } } const styles = StyleSheet.create({ cell: { height: 68, paddingLeft: 11, paddingRight: 15, flexDirection: "row", backgroundColor: F8Colors.bianca, alignItems: "center" }, name: { flex: 1, fontSize: 17, marginHorizontal: 13, color: F8Colors.tangaroa }, private: { color: F8Colors.lightText } }); module.exports = FriendCell; ================================================ FILE: js/tabs/schedule/FriendsListView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import EmptySchedule from "./EmptySchedule"; import React from "react"; import InviteFriendsButton from "./InviteFriendsButton"; import PureListView from "../../common/PureListView"; import FriendCell from "./FriendCell"; import { View } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import LoginButton from "../../common/LoginButton"; type Friend = any; type Props = { friends: Array, navigator: Navigator }; class FriendsListView extends React.Component { props: Props; _innerRef: ?PureListView; constructor(props: Props) { super(props); this._innerRef = null; (this: any).renderRow = this.renderRow.bind(this); (this: any).renderFooter = this.renderFooter.bind(this); (this: any).renderEmptyList = this.renderEmptyList.bind(this); (this: any).storeInnerRef = this.storeInnerRef.bind(this); } render() { return ( ); } renderRow(friend: Friend) { return ( this.openFriendsSchedule(friend)} /> ); } renderEmptyList(containerHeight: number): ?ReactElement { if (containerHeight === 0) { return null; } if (!this.props.loggedIn) { return ( ); } return ( ); } renderFooter() { return ( ); } openFriendsSchedule(friend: Friend) { this.props.navigator.push({ friend }); } storeInnerRef(ref: ?PureListView) { this._innerRef = ref; } scrollTo(...args: Array) { this._innerRef && this._innerRef.scrollTo(...args); } getScrollResponder(): any { return this._innerRef && this._innerRef.getScrollResponder(); } } module.exports = FriendsListView; ================================================ FILE: js/tabs/schedule/FriendsScheduleView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import EmptySchedule from "./EmptySchedule"; import FilterSessions from "./filterSessions"; import ListContainer from "../../common/ListContainer"; import ScheduleListView from "./ScheduleListView"; import { View, StatusBar } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import MessengerChatHead from "../../common/MessengerChatHead"; import { connect } from "react-redux"; import type { Session } from "../../reducers/sessions"; import type { FriendsSchedule } from "../../reducers/friendsSchedules"; import F8TimelineBackground from "../../common/F8TimelineBackground"; import F8Colors from "../../common/F8Colors"; import { createSelector } from "reselect"; type Props = { sessions: Array, friend: FriendsSchedule, navigator: Navigator }; class FriendsScheduleView extends React.Component { props: Props; constructor(props) { super(props); (this: any).renderEmptyList = this.renderEmptyList.bind(this); } render() { const backItem = { title: "Back", layout: "icon", icon: require("../../common/img/header/back-blue.png"), onPress: () => this.props.navigator.pop() }; const firstName = this.props.friend.name.split(" ")[0]; return ( } navigator={this.props.navigator} /> } navigator={this.props.navigator} /> ); } renderEmptyList(day, containerHeight) { return ( ); } } const data = createSelector( store => store.sessions, (store, props) => props.friend.schedule, (sessions, schedule) => FilterSessions.bySchedule(sessions, schedule) ); function select(store, props) { return { sessions: data(store, props) }; } module.exports = connect(select)(FriendsScheduleView); ================================================ FILE: js/tabs/schedule/FriendsUsingApp.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { connect } from "react-redux"; import { View, Image, StyleSheet } from "react-native"; import F8Colors from "../../common/F8Colors"; import { Text } from "../../common/F8Text"; const PROFILE_PICTURE_SIZE = 30; class FriendsUsingApp extends React.Component { props: { friends: Array<{ id: string, name: string }> }; static defaultProps = { multiline: false, photoBorderColor: F8Colors.bianca, textColor: F8Colors.colorWithAlpha("tangaroa", 0.6) }; render() { const { friends } = this.props; if (friends.length === 0) { return null; } else { return this.props.multiline ? this.renderMultiline() : this.renderSingleRow(); } } renderSingleRow() { const { friends, photoBorderColor, textColor } = this.props; const pictures = friends .slice(0, 2) .map(friend => ( )); let text = `${friends.length} friends are using the F8 app.`; if (friends.length === 1) { text = `${friends[0].name.split(" ")[0]} is using the F8 app.`; } return ( {pictures} {text} ); } renderMultiline() { const { friends, textColor } = this.props; let names = []; let pictures = []; const showFriendsDetailsCount = 2; friends.slice(0, showFriendsDetailsCount).map((friend, idx) => { names.push(friend.name); pictures.push( ); }); const text = this.getLabelWithFriendNames(names, friends); return ( {pictures} {text} ); } getLabelWithFriendNames(names = [], friends = []) { if (friends.length === 1) { return `${friends[0].name} is using the F8 app.`; } if (names.length - friends.length === 0) { return `${friends.length} friends are using the F8 app.`; } const othersNumber = friends.length - names.length; const pluralizeOthers = othersNumber > 1 ? `${othersNumber} others are` : `${othersNumber} other friend are`; return `${names.join(", ")} and ${pluralizeOthers} using the F8 app.`; } } const styles = StyleSheet.create({ container: { alignItems: "center" }, profilePic: { width: PROFILE_PICTURE_SIZE, height: PROFILE_PICTURE_SIZE, borderRadius: PROFILE_PICTURE_SIZE / 2 }, text: { fontSize: 13 } }); function select(store) { return { friends: store.friendsSchedules }; } module.exports = connect(select)(FriendsUsingApp); ================================================ FILE: js/tabs/schedule/GeneralScheduleView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import EmptySchedule from "./EmptySchedule"; import FilterHeader from "./FilterHeader"; import FilterSessions from "./filterSessions"; import ListContainer from "../../common/ListContainer"; import React from "react"; import ScheduleListView from "./ScheduleListView"; import FilterScreen from "../../filter/FilterScreen"; import { connect } from "react-redux"; import { switchDay, applyScheduleFilter, clearScheduleFilter } from "../../actions"; import type { Session } from "../../reducers/sessions"; import { Dimensions } from "react-native"; import { Platform, View } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import F8TimelineBackground from "../../common/F8TimelineBackground"; import HideCompleted from "./HideCompleted"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import F8ScheduleGantt from "./F8ScheduleGantt"; import { sessionsHappeningToday } from "../../common/convertTimes"; // TODO: Move from reselect to memoize? import { createSelector } from "reselect"; const data = createSelector( store => store.sessions, store => store.scheduleFilter, (sessions, filter) => FilterSessions.byTopics(sessions, filter) ); const GANTT_PADDDING_H = 14, GANTT_WIDTH = Dimensions.get("window").width - GANTT_PADDDING_H * 2; type Props = { filter: any, day: number, sessions: Array, navigator: Navigator, logOut: () => void, switchDay: (day: number) => void }; class GeneralScheduleView extends React.Component { props: Props; constructor(props) { super(props); (this: any).renderEmptyList = this.renderEmptyList.bind(this); (this: any).switchDay = this.switchDay.bind(this); (this: any).openFilterScreen = this.openFilterScreen.bind(this); (this: any).renderStickyHeader = this.renderStickyHeader.bind(this); this.state = { hideCompleted: false, filterModal: false, sessionsHappeningToday: sessionsHappeningToday(props.now), incompleteSessions: FilterSessions.byCompleted(props.sessions, props.now) }; } componentWillReceiveProps(nextProps) { if ( nextProps.sessions !== this.props.sessions || nextProps.now !== this.props.now ) { this.setState({ sessionsHappeningToday: sessionsHappeningToday(nextProps.now), incompleteSessions: FilterSessions.byCompleted( nextProps.sessions, nextProps.now ) }); } } render() { let sessions = [...this.props.sessions]; if (this.state.hideCompleted && this.state.sessionsHappeningToday) { sessions = [...this.state.incompleteSessions]; } const content = ( this.props.navigator && this.props.navigator.push({ maps: true }) }} rightItem={{ icon: require("../../common/img/header/filter.png"), title: "Filter", onPress: this.openFilterScreen }} > this.renderGanttChart(1, sessions)} renderFooter={_ => } navigator={this.props.navigator} /> this.renderGanttChart(2, sessions)} renderFooter={_ => } navigator={this.props.navigator} /> ); if (Platform.OS === "ios") { return content; } else { return ( {content} this.setState({ filterModal: false })} onApply={selected => this.props.filterTopics(selected)} /> ); } } renderStickyHeader() { let hideCompletedBar; if (this.state.sessionsHappeningToday) { hideCompletedBar = ( this.setState({ hideCompleted })} /> ); } let filterHeader; if (Object.keys(this.props.filter).length > 0) { filterHeader = ( this.props.clearFilter()} /> ); } return ( {hideCompletedBar} {filterHeader} ); } renderGanttChart(day: number, sessions: Array) { const hasFilters = Object.keys(this.props.filter).length > 0; if (hasFilters || !sessions.length) { // intercept when list is filtered // also disallow empty sessions to prevent the overflow color view from rendering return ; // TODO: better solution than spacer view } return ( ); } renderEmptyList(day: number, containerHeight: number) { const otherDay = day === 1 ? 2 : 1; const dayDir = day === 1 ? "left" : "right"; return ( ); } openFilterScreen() { if (Platform.OS === "ios") { this.props.navigator.push({ filter: true, topics: this.props.topics, selectedTopics: this.props.filter, onApply: selected => this.props.filterTopics(selected) }); } else { this.setState({ filterModal: true }); } } switchDay(page: number) { this.props.switchDay(page + 1); } } function select(store) { return { day: store.navigation.day, filter: store.scheduleFilter, topics: store.scheduleTopics, sessions: data(store) }; } function actions(dispatch) { return { switchDay: day => dispatch(switchDay(day)), filterTopics: selected => dispatch(applyScheduleFilter(selected)), clearFilter: _ => dispatch(clearScheduleFilter()) }; } module.exports = connect(select, actions)(GeneralScheduleView); ================================================ FILE: js/tabs/schedule/HideCompleted.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import { Platform, StyleSheet, View, Switch, Alert } from "react-native"; import { Text } from "../../common/F8Text"; const CONTAINER_HEIGHT = 46; /* ============================================================================= ============================================================================= */ class HideCompleted extends React.Component { static defaultProps = { enabled: false, label: "Hide completed", backgroundColor: F8Colors.persianBlue, textColor: F8Colors.white, switchInactiveColor: F8Colors.colorWithAlpha("tangaroa", 0.8), switchActiveColor: F8Colors.blue, onChange: _ => {} }; render() { const { label, backgroundColor, textColor } = this.props; return ( {label.toUpperCase()} {this.renderSwitch()} ); } renderSwitch() { const { enabled, onChange, switchInactiveColor } = this.props; if (Platform.OS === "ios") { return ( ); } else { return ( ); } } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ container: { height: CONTAINER_HEIGHT, paddingHorizontal: 18, flexDirection: "row", alignItems: "center" }, text: { flex: 1, fontSize: 13, fontFamily: F8Fonts.helvetica }, toggleSwitch: { flex: 0 }, toggleBackground: { position: "absolute", left: 0, top: 0, right: 0, bottom: 0, borderRadius: 20 } }); /* playground cards ========================================================= */ const hideCompleted = HideCompleted; hideCompleted.__cards__ = define => { define("Default", _ => ); define("Customized", _ => ( Alert.alert(val)} label="Customized Example" enabled={false} backgroundColor={F8Colors.turquoise} textColor={F8Colors.tangaroa} switchInactiveColor={F8Colors.pink} switchActiveColor={F8Colors.yellow} /> )); }; /* exports ================================================================== */ module.exports = hideCompleted; ================================================ FILE: js/tabs/schedule/InviteFriendsButton.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import F8Button from "../../common/F8Button"; import { AppInviteDialog } from "react-native-fbsdk"; import { connect } from "react-redux"; import F8Analytics from "../../F8Analytics"; import { Alert } from "react-native"; class InviteFriendsButton extends React.Component { props: { appLinkURL: string, appInvitePreviewImageURL: string, style: any }; render() { if (!this.props.appLinkURL) { return null; } return ( this.inviteFriends()} /> ); } inviteFriends() { F8Analytics.logEvent("Invite Friends", 1); AppInviteDialog.show({ applinkUrl: this.props.appLinkURL, previewImageUrl: this.props.appInvitePreviewImageURL }).catch(error => { if (error.message) { Alert.alert(error.message); } }); } } function select(store) { return { appLinkURL: store.config.appLinkURL, appInvitePreviewImageURL: store.config.appInvitePreviewImageURL }; } module.exports = connect(select)(InviteFriendsButton); ================================================ FILE: js/tabs/schedule/MyScheduleView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import EmptySchedule from "./EmptySchedule"; import F8Button from "../../common/F8Button"; import FilterSessions from "./filterSessions"; import ListContainer from "../../common/ListContainer"; import LoginButton from "../../common/LoginButton"; import React from "react"; import ScheduleListView from "./ScheduleListView"; import FriendsListView from "./FriendsListView"; import { connect } from "react-redux"; import { View, StatusBar } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import F8Colors from "../../common/F8Colors"; import StyleSheet from "../../common/F8StyleSheet"; import F8TimelineBackground from "../../common/F8TimelineBackground"; import { HeaderTitle } from "../../common/F8Text"; import PrivacyIcon from "./PrivacyIcon"; import HideCompleted from "./HideCompleted"; import F8Tooltip from "../../common/F8Tooltip"; import { sessionsHappeningToday } from "../../common/convertTimes"; import { logOutWithPrompt, switchTab, switchDay, loadFriendsSchedules } from "../../actions"; import type { Session } from "../../reducers/sessions"; import type { FriendsSchedule } from "../../reducers/friendsSchedules"; import type { State as User } from "../../reducers/user"; import type { State as Schedule } from "../../reducers/schedule"; import { createSelector } from "reselect"; const PRIVACY_ICON_SIZE = 20; type Props = { user: User, sessions: Array, friends: Array, schedule: Schedule, navigator: Navigator, logOut: () => void, jumpToSchedule: (day: number) => void, loadFriends: () => void }; // TODO: Rename to MyF8View class MyScheduleView extends React.Component { props: Props; constructor(props) { super(props); (this: any).renderEmptySessionsList = this.renderEmptySessionsList.bind( this ); (this: any).openSharingSettings = this.openSharingSettings.bind(this); (this: any).handleSegmentChanged = this.handleSegmentChanged.bind(this); (this: any).dismiss = this.dismiss.bind(this); (this: any).renderStickyHeader = this.renderStickyHeader.bind(this); this.state = { hideCompleted: false, privacyModal: false, showStickyHeader: true, tooltipX: 0, tooltipY: 0, sessionsHappeningToday: sessionsHappeningToday(props.now), incompleteSessions: FilterSessions.byCompleted(props.sessions, props.now) }; } componentWillReceiveProps(nextProps) { if ( nextProps.sessions !== this.props.sessions || nextProps.now !== this.props.now ) { this.setState({ sessionsHappeningToday: sessionsHappeningToday(nextProps.now), incompleteSessions: FilterSessions.byCompleted( nextProps.sessions, nextProps.now ) }); } } render() { let rightItem; if (this.props.user.isLoggedIn) { rightItem = { title: "Settings", layout: "icon", icon: require("../../common/img/header/settings.png"), onPress: this.openSharingSettings }; } return ( this.props.navigator && this.props.navigator.push({ maps: true }) }} rightItem={rightItem} headerBackgroundColor={F8Colors.pink} headerTitleColor={F8Colors.yellow} headerItemsColor={F8Colors.white} segmentedTextColor={F8Colors.white} segmentedBorderColor={F8Colors.yellow} stickyHeader={this.renderStickyHeader()} > {this.renderContent()} {this.renderPrivacyModal()} ); } renderContent() { let sessions = [...this.props.sessions]; if (this.state.hideCompleted && this.state.sessionsHappeningToday) { sessions = [...this.state.incompleteSessions]; } return [ } renderEmptyList={this.renderEmptySessionsList} renderFooter={_ => } navigator={this.props.navigator} />, } renderEmptyList={this.renderEmptySessionsList} renderFooter={_ => } navigator={this.props.navigator} />, ]; } renderHeaderContent() { const { user } = this.props; const isPrivate = user && user.isLoggedIn && !user.sharedSchedule; const privateStyles = isPrivate ? styles.titlePrivate : null; let privacyIcon; if (isPrivate) { privacyIcon = ( { this._titleContentWrapper.measureInWindow((x, y, width, height) => { const tooltipX = Math.round(x + width - PRIVACY_ICON_SIZE / 2), tooltipY = Math.round(y + height); this.setState({ tooltipX, tooltipY, privacyModal: true }); }); }} /> ); } return ( (this._titleContentWrapper = c)} collapsable={false} > My F8 {privacyIcon} ); } renderStickyHeader() { if (this.state.showStickyHeader && sessionsHappeningToday(this.props.now)) { return ( this.setState({ hideCompleted })} enabled={this.state.hideCompleted} /> ); } else { return null; } } renderNotLoggedIn(containerHeight: number) { if (containerHeight === 0) { return null; } return ( ); } renderEmptySessionsList(day: number, containerHeight: number) { if (!this.props.user.isLoggedIn) { return this.renderNotLoggedIn(containerHeight); } const todaySessions = this.props.sessions.filter(s => s.day === day); const todayIncomplete = FilterSessions.byCompleted( todaySessions, this.props.now ); if (todaySessions.length > 0 && todayIncomplete.length === 0) { // there are sessions but they're complete return ( 0 ? { height: containerHeight } : null} key="schedule" text={`Your Day ${day} sessions have completed.\nThanks for joining us!`} /> ); } else if (day === 1) { return ( 0 ? { height: containerHeight } : null} key="schedule" image={require("./img/empty-header-1.png")} text={`You haven’t added\nany Day ${day} sessions yet.`} > this.props.jumpToSchedule(1)} /> ); } else { return ( 0 ? { height: containerHeight } : null} key="schedule" image={require("./img/empty-header-2.png")} text={"Sessions you add\nwill appear here."} > this.props.jumpToSchedule(2)} /> ); } } renderPrivacyModal() { return ( this.setState({ privacyModal: false })} /> ); } openSharingSettings() { this.props.navigator.push({ shareSettings: 1 }); } dismiss() { this.props.navigator.pop(); } handleSegmentChanged(segment: number) { if (segment === 2 /* friends */) { const { user, loadFriends } = this.props; user.isLoggedIn && loadFriends(); this.setState({ showStickyHeader: false }); } else if (!this.state.showStickyHeader) { this.setState({ showStickyHeader: true }); } } } const styles = StyleSheet.create({ titleContainer: { alignItems: "center", justifyContent: "center", flex: 1 }, titleContent: { alignItems: "center", alignSelf: "flex-start", flexDirection: "row" }, titleText: { color: F8Colors.yellow }, titlePrivate: { marginRight: 9, ios: { marginLeft: 29 // fake centering } } }); const data = createSelector( store => store.sessions, store => store.schedule, (sessions, schedule) => FilterSessions.bySchedule(sessions, schedule) ); function select(store) { return { user: store.user, sessions: data(store), schedule: store.schedule, // Only show friends who have something in their schedule friends: store.friendsSchedules.filter( friend => friend.schedule && Object.keys(friend.schedule).length > 0 ) }; } function actions(dispatch) { return { logOut: () => dispatch(logOutWithPrompt()), jumpToSchedule: day => dispatch([switchTab("schedule"), switchDay(day)]), loadFriends: () => dispatch(loadFriendsSchedules()) }; } module.exports = connect(select, actions)(MyScheduleView); ================================================ FILE: js/tabs/schedule/PrivacyIcon.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, Image, TouchableOpacity, Alert } from "react-native"; import StyleSheet from "../../common/F8StyleSheet"; /* Config ============================================================================= */ type Props = { network: string, password: string }; type State = {}; const DEFAULT_SIZE = 48, DEFAULT_BGCOLOR = "#374B71", ICON_COLOR = "white", ICON_WIDTH = 20, ICON_HEIGHT = 25, ICON_WIDTH_RATIO = ICON_WIDTH / DEFAULT_SIZE, ICON_HEIGHT_RATIO = ICON_HEIGHT / DEFAULT_SIZE; /* ============================================================================= -------------------------------------------------------------------------------- Props: ? size:number -> Icon size ? backgroundColor:string -> WiFi network password ? onPress:function -> Callback ? style -> Pass-through container styles ============================================================================= */ class PrivacyIcon extends React.Component { props: Props; state: State = {}; static defaultProps = { size: DEFAULT_SIZE, backgroundColor: DEFAULT_BGCOLOR, iconColor: ICON_COLOR }; render() { const { onPress, backgroundColor, size, iconColor } = this.props; const iconWidth = size * ICON_WIDTH_RATIO; const iconHeight = size * ICON_HEIGHT_RATIO; const containerStyles = [ styles.container, { backgroundColor, width: size, height: size, borderRadius: size / 2 }, this.props.style ]; if (onPress) { return ( {this.renderIconImage(iconWidth, iconHeight, iconColor)} ); } else { return ( {this.renderIconImage(iconWidth, iconHeight, iconColor)} ); } } renderIconImage(width, height, tintColor) { return ( ); } } /* Styles ============================================================================= */ const styles = StyleSheet.create({ container: { alignItems: "center", justifyContent: "center" } }); /* Playground Cards ============================================================================= */ const privacyIcon = PrivacyIcon; privacyIcon.__cards__ = define => { define("Default", () => ); define("Touchable", () => ( Alert.alert(" pressed!")} /> )); define("Customized", () => ( Alert.alert(" pressed!")} /> )); }; /* Export ============================================================================= */ module.exports = privacyIcon; ================================================ FILE: js/tabs/schedule/ProfileButton.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import { Image, StyleSheet } from "react-native"; class ProfileButton extends React.Component { render() { return ( ); } } const styles = StyleSheet.create({ profilePic: { width: 30, height: 30, borderRadius: 15 } }); module.exports = ProfileButton; ================================================ FILE: js/tabs/schedule/ScheduleListView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import F8SessionCell from "./F8SessionCell"; import FilterSessions from "./filterSessions"; import { Navigator } from "react-native-deprecated-custom-components"; import React from "react"; import SessionsSectionHeader from "./SessionsSectionHeader"; import PureListView from "../../common/PureListView"; import groupSessions from "./groupSessions"; import type { Session } from "../../reducers/sessions"; import type { SessionsListData } from "./groupSessions"; type Props = { day: number, sessions: Array, navigator: Navigator, renderEmptyList?: (day: number) => ReactElement }; type State = { todaySessions: SessionsListData }; class ScheduleListView extends React.Component { props: Props; state: State; _innerRef: ?PureListView; constructor(props: Props) { super(props); this.state = { todaySessions: groupSessions( FilterSessions.byDay(props.sessions, props.day) ) }; this._innerRef = null; (this: any).renderSectionHeader = this.renderSectionHeader.bind(this); (this: any).renderRow = this.renderRow.bind(this); (this: any).renderEmptyList = this.renderEmptyList.bind(this); (this: any).storeInnerRef = this.storeInnerRef.bind(this); } componentWillReceiveProps(nextProps: Props) { if ( nextProps.sessions !== this.props.sessions || nextProps.day !== this.props.day ) { this.setState({ todaySessions: groupSessions( FilterSessions.byDay(nextProps.sessions, nextProps.day) ) }); } } render() { return ( ); } renderSectionHeader(sectionData: any, sectionID: string) { let formatted = sectionID .toLowerCase() .replace("am", "") .replace("pm", "") || sectionID; return ; } renderRow(session: Session, day: number) { return ( this.openSession(session, day)} session={session} firstRow={this.isFirstSessionCell(session.id)} /> ); } renderEmptyList(containerHeight: number): ?ReactElement { // if listview onLayout hasn't updated container height, don't bother if (containerHeight === 0) { return null; } // TODO: different fix // otherwise render fallback cta with a valid and centerable height const { renderEmptyList } = this.props; return renderEmptyList && renderEmptyList(this.props.day, containerHeight); } openSession(session: Session, day: number) { let allSessions = { ...this.state.todaySessions }; this.props.navigator.push({ day, session, allSessions }); } storeInnerRef(ref: ?PureListView) { this._innerRef = ref; } scrollTo(...args: Array) { this._innerRef && this._innerRef.scrollTo(...args); } getScrollResponder(): any { return this._innerRef && this._innerRef.getScrollResponder(); } isFirstSessionCell(id) { const keys = Object.keys(this.state.todaySessions); const innerKeys = Object.keys(this.state.todaySessions[keys[0]]); return id === innerKeys[0]; } } module.exports = ScheduleListView; ================================================ FILE: js/tabs/schedule/SessionsCarousel.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import F8Analytics from "../../F8Analytics"; import React from "react"; import { Platform, StatusBar } from "react-native"; import F8SessionDetails from "./F8SessionDetails"; import F8PageControl from "../../common/F8PageControl"; import F8Header from "../../common/F8Header"; import StyleSheet from "../../common/F8StyleSheet"; import formatTime from "./formatTime"; import Carousel from "../../common/Carousel"; import { connect } from "react-redux"; import { loadFriendsSchedules, shareSession } from "../../actions"; import type { Dispatch } from "../../actions/types"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import { Text, View, Navigator } from "react-native"; import type { Session } from "../../reducers/sessions"; type Context = { rowIndex: number, // TODO: IndexWithinSection sectionLength: number, sectionTitle: string }; type Props = { allSessions?: { [sectionID: string]: { [sessionID: string]: Session } }, session: Session, navigator: Navigator, dispatch: Dispatch }; class SessionsCarusel extends React.Component { props: Props; state: { day: number, count: number, selectedIndex: number, flatSessionsList: Array, contexts: Array }; constructor(props: Props) { super(props); const flatSessionsList = []; const contexts: Array = []; let allSessions = this.props.allSessions; if (!allSessions) { const { session } = this.props; allSessions = { [formatTime(session.startTime)]: { [session.id]: session } }; } // TODO: Add test for (let sectionID in allSessions) { const sectionLength = Object.keys(allSessions[sectionID]).length; let rowIndex = 0; for (let sessionID in allSessions[sectionID]) { const session = allSessions[sectionID][sessionID]; flatSessionsList.push(session); contexts.push({ rowIndex, sectionLength, sectionTitle: sectionID }); rowIndex++; } } const selectedIndex = flatSessionsList.findIndex( s => s.id === this.props.session.id ); this.state = { day: this.props.session.day, count: flatSessionsList.length, selectedIndex, flatSessionsList, contexts }; (this: any).dismiss = this.dismiss.bind(this); (this: any).handleIndexChange = this.handleIndexChange.bind(this); (this: any).renderCard = this.renderCard.bind(this); (this: any).shareCurrentSession = this.shareCurrentSession.bind(this); } render() { const { rowIndex, sectionLength, sectionTitle } = this.state.contexts[ this.state.selectedIndex ]; const backItem = { title: "Back", layout: "icon", icon: require("../../common/img/header/back.png"), onPress: _ => this.props.navigator.pop() }; const rightItem = { title: "Share", layout: "icon", icon: require("../../common/img/header/share.png"), onPress: this.shareCurrentSession }; return ( {`DAY ${this.state.day}`} {sectionTitle.toLowerCase()} ); } renderCard(index: number): ReactElement { // const iOSKey = Platform.OS === 'ios' ? { key: `SCC_${this.state.flatSessionsList[index].id}`} : {}; return ( ); } shareCurrentSession() { const session = this.state.flatSessionsList[this.state.selectedIndex]; this.props.dispatch(shareSession(session)); } componentDidMount() { this.track(this.state.selectedIndex); this.props.dispatch(loadFriendsSchedules()); } dismiss() { this.props.navigator.pop(); } handleIndexChange(selectedIndex: number) { this.track(selectedIndex); this.setState({ selectedIndex }); } track(index: number) { const { id } = this.state.flatSessionsList[index]; F8Analytics.logEvent("View Session", 1, { id }); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: F8Colors.white }, headerContent: { android: { flex: 1, alignItems: "flex-start", justifyContent: "flex-end", paddingBottom: 9 }, ios: { marginTop: -5, alignItems: "center" // justifyContent: 'center', } }, title: { ios: { textAlign: "center" } }, day: { color: F8Colors.yellow, fontFamily: F8Fonts.fontWithWeight(F8Fonts.basis, "helveticaBold"), fontSize: 13, android: { marginBottom: -4 } }, time: { color: F8Colors.white, fontFamily: F8Fonts.fontWithWeight("helvetica", "semibold"), fontSize: 15, ios: { marginVertical: 2 }, android: { marginBottom: 3 } } }); module.exports = connect()(SessionsCarusel); ================================================ FILE: js/tabs/schedule/SessionsSectionHeader.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Colors from "../../common/F8Colors"; import F8Fonts from "../../common/F8Fonts"; import React from "react"; import { View, StyleSheet } from "react-native"; import { Text } from "../../common/F8Text"; class SessionsSectionHeader extends React.Component { props: { title: string }; render() { return ( {this.props.title} ); } } const styles = StyleSheet.create({ header: { // position:'relative', left: 0, width: 56, marginBottom: -30, justifyContent: "center", height: 30 }, label: { paddingTop: 6, textAlign: "right", fontFamily: F8Fonts.helvetica, fontSize: 13, color: F8Colors.tangaroa } }); module.exports = SessionsSectionHeader; ================================================ FILE: js/tabs/schedule/SharingSettingsCommon.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { View, StyleSheet } from "react-native"; import F8Colors from "../../common/F8Colors"; import PrivacyIcon from "./PrivacyIcon"; import { Heading2, Paragraph } from "../../common/F8Text"; import ProfilePicture from "../../common/ProfilePicture"; import { connect } from "react-redux"; import type { State as User } from "../../reducers/user"; class SharingSettingsCommon extends React.Component { props: { user: User, style: any }; static defaultProps = { pictureSize: 158 }; render() { const { user, pictureSize } = this.props; const isPrivate = user && !user.sharedSchedule; return ( {isPrivate ? : null} {"Let friends view your\nschedule in the F8 app?"} Friends using the F8 app will be able to view your schedule. This won’t post to Facebook. ); } } const styles = StyleSheet.create({ container: { alignItems: "center" }, image: { alignSelf: "center" }, privacy: { position: "absolute", right: 0, bottom: 0 }, content: { padding: 18, alignItems: "center" }, h2: { color: F8Colors.blue, textAlign: "center" }, p: { marginTop: 10, textAlign: "center" }, title: { marginTop: 40, flexDirection: "row", alignItems: "center", backgroundColor: "transparent" }, name: { fontSize: 12, color: "white", marginLeft: 10, fontWeight: "bold" } }); function select(store) { return { user: store.user }; } module.exports = connect(select)(SharingSettingsCommon); ================================================ FILE: js/tabs/schedule/SharingSettingsModal.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { Dimensions, StyleSheet, View, Image } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import F8Colors from "../../common/F8Colors"; import F8Button from "../../common/F8Button"; import { Heading2, Paragraph } from "../../common/F8Text"; import F8BackgroundRepeat from "../../common/F8BackgroundRepeat"; import ProfilePicture from "../../common/ProfilePicture"; import FriendsUsingApp from "./FriendsUsingApp"; import { setSharingEnabled } from "../../actions"; import { connect } from "react-redux"; import F8Modal from "../../common/F8Modal"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, WINDOW_HEIGHT = Dimensions.get("window").height, VERTICAL_BREAKPOINT = WINDOW_HEIGHT <= 600, CONTENT_SPACING_SCALE = VERTICAL_BREAKPOINT ? 0.5 : 1, MODAL_PADDING_H = 10, MODAL_WIDTH = WINDOW_WIDTH - MODAL_PADDING_H * 2, HEADER_HEIGHT = 177, PROFILE_PICTURE_SIZE = 70; /* ============================================================================= */ class SharingSettingsModal extends React.Component { props: { navigator: Navigator, dispatch: () => void }; render() { return ( ); } renderContent = _ => { return ( {"Let friends view your\nschedule in the F8 app?"} Friends using the F8 app will be able to view your custom schedule. this.handleSetSharing(true)} /> this.handleSetSharing(false)} /> ); }; renderFooter = _ => { return ( ); }; handleSetSharing(enabled: boolean) { this.props.dispatch(setSharingEnabled(enabled)); this.props.onSetSharing && this.props.onSetSharing(enabled); // this.props.navigator.pop(); } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ header: { alignSelf: "stretch" }, headerBackground: { position: "absolute", left: 0, top: 0 }, headerIllustration: { alignSelf: "center", width: 275, height: HEADER_HEIGHT }, profilePicture: { position: "absolute", left: 0, right: 0, bottom: 0, alignItems: "center" }, content: { paddingVertical: 26 * CONTENT_SPACING_SCALE, paddingHorizontal: 30 }, h2: { color: F8Colors.blue, textAlign: "center", marginBottom: 10 }, p: { textAlign: "center", marginBottom: 15 }, button: { marginTop: 20 * CONTENT_SPACING_SCALE, marginBottom: 10 * CONTENT_SPACING_SCALE, alignSelf: "stretch" } }); /* redux select ============================================================= */ function select(store) { return { user: store.user }; } /* exports ================================================================== */ module.exports = connect(select)(SharingSettingsModal); ================================================ FILE: js/tabs/schedule/SharingSettingsScreen.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import F8Colors from "../../common/F8Colors"; import React from "react"; import { Dimensions, View, Switch, StyleSheet } from "react-native"; import { Navigator } from "react-native-deprecated-custom-components"; import { Text } from "../../common/F8Text"; import FriendsUsingApp from "./FriendsUsingApp"; import F8Header from "../../common/F8Header"; import SharingSettingsCommon from "./SharingSettingsCommon"; import { setSharingEnabled, logOutWithPrompt } from "../../actions"; import { connect } from "react-redux"; import type { State as User } from "../../reducers/user"; // more compact on shorter devices const WIN_HEIGHT = Dimensions.get("window").height; const FRIENDS_AREA_SCALING = WIN_HEIGHT <= 600 ? 0.5 : 1; const PROFILE_PICTURE_SIZE = WIN_HEIGHT <= 600 ? 120 : 158; class SharingSettingsScreen extends React.Component { props: { navigator: Navigator, dispatch: () => void, sharedSchedule: boolean, user: User }; render() { const navItem = { icon: require("../../common/img/header/back.png"), title: "Back", layout: "icon", onPress: () => this.props.navigator.pop() }; const rightItem = { icon: require("../../common/img/header/logout.png"), title: "Log out", onPress: () => this.props.dispatch(logOutWithPrompt()) }; return ( NO this.props.dispatch(setSharingEnabled(enabled))} onTintColor={F8Colors.green} /> YES ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center" }, switchWrapper: { flexDirection: "row", alignItems: "center" }, switch: { margin: 10 }, option: { fontSize: 12, color: F8Colors.lightText }, friends: { paddingTop: 20 * FRIENDS_AREA_SCALING, paddingHorizontal: 50 * FRIENDS_AREA_SCALING, paddingBottom: 40 * FRIENDS_AREA_SCALING } }); function select(store) { return { user: store.user, sharedSchedule: store.user.sharedSchedule }; } module.exports = connect(select)(SharingSettingsScreen); ================================================ FILE: js/tabs/schedule/__tests__/formatDuration-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; jest.autoMockOff(); import formatDuration from "../formatDuration"; describe("formatDuration", () => { it("formats duration", () => { expect(formatDuration(0, 3600000)).toEqual("1 hour"); expect(formatDuration(0, 7200000)).toEqual("2 hours"); expect(formatDuration(0, 1800000)).toEqual("30 min"); expect(formatDuration(0, 3601000)).toEqual("1 hour 1 min"); expect(formatDuration(0, 1427371200000)).toEqual("Until 5:00am"); }); }); ================================================ FILE: js/tabs/schedule/__tests__/formatTime-test.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; jest.dontMock("../formatTime"); import formatTime from "../formatTime"; describe("formatTime", () => { it("formats time", () => { expect(formatTime(1427371200000)).toEqual("5:00am"); expect(formatTime(1427373900000)).toEqual("5:45am"); }); }); ================================================ FILE: js/tabs/schedule/filterSessions.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import type { Session } from "../../reducers/sessions"; type StringMap = { [key: string]: boolean }; function byDay(sessions: Array, day: number): Array { return sessions.filter(session => session.day === day); } function byTopics(sessions: Array, topics: StringMap): Array { if (Object.keys(topics).length === 0) { return sessions; } return sessions.filter(session => { let hasMatchingTag = false; session.tags.forEach(tag => { hasMatchingTag = hasMatchingTag || topics[tag]; }); return hasMatchingTag; }); } function bySchedule( sessions: Array, schedule: StringMap ): Array { return sessions.filter(session => schedule[session.id]); } function byCompleted(sessions: Array, time: number): Array { return sessions.filter(session => session.endTime > time); } module.exports = { byDay, byTopics, bySchedule, byCompleted }; ================================================ FILE: js/tabs/schedule/formatDuration.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import formatTime from "./formatTime"; function naivePlural(text: string, count: number): string { if (count > 1) { return text + "s"; } return text; } function formatDuration(startMs: number, endMs: number): string { let ms = endMs - startMs; let minutes = ms / 1000 / 60; let hours = Math.floor(minutes / 60); if (hours > 2) { return "Until " + formatTime(endMs).toLowerCase(); } let durationText = ""; if (hours > 0) { durationText = hours + " " + naivePlural("hour", hours) + " "; minutes = minutes - hours * 60; } if (minutes > 0) { durationText = durationText + Math.ceil(minutes) + " min"; } return durationText.trim(); } module.exports = formatDuration; ================================================ FILE: js/tabs/schedule/formatTime.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import moment from "moment-timezone"; import { timezone } from "../../env.js"; function formatTime(unix: number, hideAMPM: boolean): string { return moment.tz(unix, timezone).format(hideAMPM ? "h:mm" : "h:mma"); } module.exports = formatTime; ================================================ FILE: js/tabs/schedule/groupSessions.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import type { Session } from "../../reducers/sessions"; import formatTime from "./formatTime"; export type SessionsListData = { [time: string]: { [sessionID: string]: Session } }; function groupSessions(sessions: Array): SessionsListData { const data = {}; sessions.forEach(session => { const timeSectionKey = session.allDay ? "All Day" : formatTime(session.startTime); data[timeSectionKey] = data[timeSectionKey] || {}; data[timeSectionKey][session.id] = session; }); return data; } module.exports = groupSessions; ================================================ FILE: js/tabs/videos/F8EmptyVideosView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; /* Dependencies ============================================================================= */ import React from "react"; import F8Colors from "../../common/F8Colors"; import { Dimensions, View, Image } from "react-native"; import { Text } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; import F8Button from "../../common/F8Button"; const HEADER_IMAGE_RATIO = 257 / 375, WIN_WIDTH = Dimensions.get("window").width, HEADER_IMAGE_WIDTH = WIN_WIDTH, HEADER_IMAGE_HEIGHT = HEADER_IMAGE_WIDTH * HEADER_IMAGE_RATIO; /* ============================================================================= */ class F8EmptyVideosView extends React.Component { static defaultProps = { onPress: _ => {} }; render() { return ( F8 2017 videos will be added here on the evening of Day 1. ); } } /* StyleSheet ============================================================================= */ const styles = StyleSheet.create({ container: { flex: 1, paddingVertical: 20 }, content: { paddingHorizontal: 46 }, text: { marginTop: 22, marginBottom: 48, fontSize: 17, lineHeight: 27, color: F8Colors.tangaroa, textAlign: "center" } }); /* Exports ============================================================================= */ export default F8EmptyVideosView; ================================================ FILE: js/tabs/videos/F8VideoThumb.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import { Dimensions, View, Image, TouchableOpacity } from "react-native"; import { Text } from "../../common/F8Text"; import StyleSheet from "../../common/F8StyleSheet"; import F8Fonts from "../../common/F8Fonts"; /* constants ================================================================ */ const WINDOW_WIDTH = Dimensions.get("window").width, CONTAINER_PADDING_H = 15, GUTTER = 8, ROW_SPACING = 40, WIDTH_LARGE = WINDOW_WIDTH - CONTAINER_PADDING_H * 2, WIDTH_SMALL = (WINDOW_WIDTH - CONTAINER_PADDING_H * 2 - GUTTER) / 2, IMAGE_ASPECT_RATIO_SMALL = 99 / 169, IMAGE_ASPECT_RATIO_LARGE = 202 / 344, NUMLINES_SMALL = 3, NUMLINES_LARGE = 2; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {?String} type Layout style (default "small") * @param {?String} title Video title * @param {?String} length Video length * @param {?String} image Thumbnail image source * @param {?Boolean} watched Has video already been watched by user * @param {?Number} activeOpacity On-tap opacity value (default 0.75) * @return {ReactElement} * ============================================================================== */ class F8VideoThumb extends React.Component { static defaultProps = { type: "small", activeOpacity: 0.75 }; render() { const { id, type, onPress, image, title, activeOpacity, length, watched } = this.props; const { imageWidth, imageHeight } = this.getImageSize(type); return ( onPress && onPress(id)} > {this.renderImage(image, imageWidth, imageHeight)} {this.renderLength(type, length, watched)} {this.renderTitle(type, title)} ); } renderImage(src, width, height) { if (src) { return ( ); } else { return ; } } renderLength(type, length, watched) { const timeDifferences = type === "large" ? styles.timeLarge : styles.timeSmall; if (length) { return {length}; } else { return null; } } renderTitle(type, title) { const titleLineLimit = type === "large" ? NUMLINES_LARGE : NUMLINES_SMALL; const titleDifferences = type === "large" ? styles.titleLarge : styles.titleSmall; if (title) { return ( {title} ); } else { return null; } } getImageSize(type) { return type === "large" ? { imageWidth: WIDTH_LARGE, imageHeight: WIDTH_LARGE * IMAGE_ASPECT_RATIO_LARGE } : { imageWidth: WIDTH_SMALL, imageHeight: WIDTH_SMALL * IMAGE_ASPECT_RATIO_SMALL }; } } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ containerLarge: { marginVertical: ROW_SPACING / 2, width: WIDTH_LARGE, paddingHorizontal: GUTTER / 2 }, containerSmall: { marginVertical: ROW_SPACING / 2, width: WIDTH_SMALL + GUTTER, paddingHorizontal: GUTTER / 2 }, image: { backgroundColor: F8Colors.tangaroa, resizeMode: "cover" }, time: { position: "absolute", right: 0, bottom: 0, backgroundColor: F8Colors.colorWithAlpha("tangaroa", 0.8), fontFamily: F8Fonts.helvetica, color: "white" }, timeSmall: { fontSize: 11, paddingVertical: 2, paddingHorizontal: 3 }, timeLarge: { fontSize: 13, paddingVertical: 3, paddingHorizontal: 5 }, title: { color: F8Colors.black }, titleSmall: { marginTop: 9, fontSize: 13, lineHeight: F8Fonts.lineHeight(17) }, titleLarge: { marginTop: 12, fontSize: 17, lineHeight: F8Fonts.lineHeight(22) } }); /* export =================================================================== */ export default F8VideoThumb; ================================================ FILE: js/tabs/videos/F8VideoView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE * * @flow */ "use strict"; import React from "react"; import F8Colors from "../../common/F8Colors"; import StyleSheet from "../../common/F8StyleSheet"; import F8Header from "../../common/F8Header"; import { StatusBar, View, ScrollView } from "react-native"; import { Heading2, Paragraph } from "../../common/F8Text"; import F8VideoPlayer from "../../video/F8VideoPlayer"; import { connect } from "react-redux"; import { shareVideo } from "../../actions"; /* ============================================================================= */ class F8VideoView extends React.Component { render() { const { video } = this.props; let rightItem; if (video.shareURL) { rightItem = { title: "Share", layout: "icon", //$FlowFixMe icon: require("../../common/img/header/share.png"), onPress: this.share }; } return ( ); } renderTitle() { const { video } = this.props; if (video.title) { return {video.title}; } else { return null; } } renderDescription() { const { video } = this.props; if (video.description) { return {video.description}; } else { return null; } } share = _ => { this.props.dispatch(shareVideo(this.props.video)); }; } /* StyleSheet =============================================================== */ const styles = StyleSheet.create({ view: { backgroundColor: F8Colors.bianca, flex: 1 }, content: { flex: 1 }, textBlock: { paddingVertical: 23, paddingHorizontal: 32 }, title: { color: F8Colors.blue, marginBottom: 5 } }); /* exports ================================================================== */ module.exports = connect()(F8VideoView); ================================================ FILE: js/tabs/videos/F8VideosView.js ================================================ /** * Copyright 2016 Facebook, Inc. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to * use, copy, modify, and distribute this software in source code or binary * form for use in connection with the web services and APIs provided by * Facebook. * * As with any software that integrates with the Facebook platform, your use * of this software is subject to the Facebook Developer Principles and * Policies [http://developers.facebook.com/policy/]. This copyright notice * shall be included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE */ "use strict"; import React from "react"; import { Platform, View } from "react-native"; import ListContainer from "../../common/ListContainer"; import PureListView from "../../common/PureListView"; import F8Colors from "../../common/F8Colors"; import F8Linking from "../../common/F8Linking"; import F8EmptyVideosView from "./F8EmptyVideosView"; import F8VideoThumb from "./F8VideoThumb"; import * as FilterVideos from "./filterVideos"; import FilterHeader from "../schedule/FilterHeader"; import FilterScreen from "../../filter/FilterScreen"; import { connect } from "react-redux"; import { applyVideoFilter, clearVideoFilter } from "../../actions"; import { createSelector } from "reselect"; /** * ============================================================================== * * ------------------------------------------------------------------------------ * @param {Array.