├── .babelrc ├── .buckconfig ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .watchmanconfig ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── app │ ├── BUCK │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── fonts │ │ │ ├── Entypo.ttf │ │ │ ├── EvilIcons.ttf │ │ │ ├── FontAwesome.ttf │ │ │ ├── Foundation.ttf │ │ │ ├── Ionicons.ttf │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ ├── MaterialIcons.ttf │ │ │ ├── Octicons.ttf │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── SimpleLineIcons.ttf │ │ │ └── Zocial.ttf │ │ ├── java │ │ └── com │ │ │ └── nearby │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── playstore-icon.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystores │ ├── BUCK │ └── debug.keystore.properties └── settings.gradle ├── app.json ├── index.js ├── ios ├── Nearby-tvOS │ └── Info.plist ├── Nearby-tvOSTests │ └── Info.plist ├── Nearby.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── Nearby-tvOS.xcscheme │ │ └── Nearby.xcscheme ├── Nearby.xcworkspace │ └── contents.xcworkspacedata ├── Nearby │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@1x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-76x76@3x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── Icon-Small-50x50@1x.png │ │ │ └── Icon-Small-50x50@2x.png │ │ ├── Contents.json │ │ ├── Launch.imageset │ │ │ ├── Contents.json │ │ │ └── Launch.jpg │ │ ├── iTunesArtwork@1x.png │ │ ├── iTunesArtwork@2x.png │ │ └── iTunesArtwork@3x.png │ ├── Info.plist │ ├── LaunchScreen.storyboard │ └── main.m ├── NearbyTests │ ├── Info.plist │ └── NearbyTests.m └── Podfile ├── jest.setup.js ├── package.json ├── release-debug.sh ├── src ├── assets │ ├── font │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Medium.ttf │ │ └── Roboto-Regular.ttf │ ├── image │ │ ├── loading.gif │ │ ├── logo.png │ │ ├── package.json │ │ └── placeholder.user.png │ └── video │ │ └── background.mp4 ├── components │ ├── general │ │ ├── Error.js │ │ ├── Loading.js │ │ ├── Placeholder.js │ │ └── WebView.js │ ├── package.json │ └── ui │ │ ├── Avatar.js │ │ ├── BackButton.js │ │ ├── Badge.js │ │ ├── Icon.js │ │ ├── Image.js │ │ ├── PostCard.js │ │ ├── Pulse.js │ │ ├── Search.js │ │ ├── SegmentButton.js │ │ ├── Spacer.js │ │ ├── TabIcon.js │ │ ├── Text.js │ │ ├── alerts │ │ ├── Alert.js │ │ ├── Toast.js │ │ └── index.js │ │ ├── index.js │ │ └── package.json ├── constants │ ├── api.js │ ├── config.js │ ├── errors.js │ ├── index.js │ └── package.json ├── containers │ ├── auth │ │ ├── AuthenticateContainer.js │ │ ├── AuthenticateView.js │ │ └── WebView.js │ ├── launch │ │ ├── LaunchContainer.js │ │ └── LaunchView.js │ ├── main │ │ ├── home │ │ │ ├── HomeContainer.js │ │ │ ├── HomeView.js │ │ │ ├── conversations │ │ │ │ ├── ConversationsListingContainer.js │ │ │ │ ├── ConversationsListingView.js │ │ │ │ └── conversation │ │ │ │ │ ├── ConversationContainer.js │ │ │ │ │ └── ConversationView.js │ │ │ └── stream │ │ │ │ ├── StreamContainer.js │ │ │ │ ├── StreamView.js │ │ │ │ └── comments │ │ │ │ ├── CommentsContainer.js │ │ │ │ └── CommentsView.js │ │ ├── system-notifications │ │ │ ├── SystemNotificationsContainer.js │ │ │ └── SystemNotificationsView.js │ │ └── user-profile │ │ │ ├── Gifts │ │ │ └── UserGiftsRender.js │ │ │ ├── Info │ │ │ └── UserInfoRender.js │ │ │ ├── Photos │ │ │ └── UserPhotosRender.js │ │ │ ├── Posts │ │ │ ├── UserPostsContainer.js │ │ │ └── UserPostsView.js │ │ │ ├── UserProfileContainer.js │ │ │ └── UserProfileView.js │ └── package.json ├── index.js ├── lib │ ├── api.js │ ├── helper.js │ ├── package.json │ └── util.js ├── navigation │ ├── auth.js │ ├── index.js │ ├── package.json │ ├── sub-scenes.js │ └── tabs.js ├── redux │ ├── core │ │ ├── conversations │ │ │ ├── actions.js │ │ │ ├── models.js │ │ │ ├── reducer.js │ │ │ └── selectors.js │ │ ├── router │ │ │ └── reducer.js │ │ ├── stream │ │ │ ├── actions.js │ │ │ ├── models.js │ │ │ ├── reducer.js │ │ │ └── selectors.js │ │ ├── system-notifications │ │ │ ├── actions.js │ │ │ ├── models.js │ │ │ ├── reducer.js │ │ │ └── selectors.js │ │ ├── user │ │ │ ├── actions.js │ │ │ └── reducer.js │ │ └── ws │ │ │ ├── actions.js │ │ │ └── reducer.js │ ├── index.js │ ├── middleware │ │ ├── analyticsMiddleware.js │ │ ├── apiMiddleware.js │ │ ├── index.js │ │ └── socketMiddleware.js │ └── package.json └── theme │ ├── colors.js │ ├── fonts.js │ ├── index.js │ ├── package.json │ ├── sizes.js │ └── styles.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"], 3 | "env": { "production": { "plugins": ["transform-remove-console"] } } 4 | } 5 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "jest": true 7 | }, 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "jsx": true, 11 | "restParams": true, 12 | "spread": true, 13 | "experimentalObjectRestSpread": true 14 | } 15 | }, 16 | "extends": [ "eslint:recommended", "plugin:react/recommended" ], 17 | "plugins": [ 18 | "babel", 19 | "react" 20 | ], 21 | "globals": { 22 | "fetch": true, 23 | "__DEV__": true 24 | }, 25 | "rules": { 26 | "indent": [2, 4, {"SwitchCase": 1}], 27 | "quotes": [2,"single"], 28 | "linebreak-style": [2,"unix"], 29 | "semi": [2,"always"], 30 | "no-console": 0, 31 | "max-len": [1,110], 32 | "comma-dangle": [2,"never"], 33 | "no-cond-assign": [2, "always"], 34 | "no-ex-assign": 2, 35 | "curly": 2, 36 | "max-depth": [2, 5], 37 | "complexity": [1, 8], 38 | "prefer-const": 0, 39 | "no-trailing-spaces": [2, {"skipBlankLines": false}], 40 | "one-var": [2, "never"], 41 | "func-names": 2, 42 | "key-spacing": [2, { 43 | "beforeColon": false, 44 | "afterColon": true 45 | }], 46 | "max-nested-callbacks": [2, 3], 47 | "new-cap": 0, 48 | "new-parens": 2, 49 | "no-mixed-spaces-and-tabs": 2, 50 | "no-multiple-empty-lines": [1, {"max": 1}], 51 | "no-nested-ternary": 2, 52 | "no-new-object": 2, 53 | "no-spaced-func": 2, 54 | "arrow-spacing": [2, {"before": true, "after": true}], 55 | "operator-assignment": [2, "always"], 56 | "operator-linebreak": [2, "after", { 57 | "overrides": { 58 | "?": "before", 59 | ":": "before" 60 | } 61 | } 62 | ], 63 | "keyword-spacing": ["error", {before: true, after: true}], 64 | "space-before-blocks": [2, "always"], 65 | "space-before-function-paren": [2, "never"], 66 | "object-curly-spacing": [2, "never"], 67 | "array-bracket-spacing": [2, "never"], 68 | "computed-property-spacing": [2, "never"], 69 | "space-in-parens": [2, "never"], 70 | "space-infix-ops": [2, {"int32Hint": true}], 71 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 72 | "no-delete-var": 2, 73 | "no-underscore-dangle": 0, 74 | "no-shadow": 2, 75 | "no-shadow-restricted-names": 2, 76 | "no-undef-init": 2, 77 | "no-undef": 2, 78 | "no-undefined": 2, 79 | "no-unused-vars": [2, { 80 | "vars": "all", 81 | "args": "after-used", 82 | "varsIgnorePattern": "hJSX" 83 | }], 84 | "yoda": [2, "never"], 85 | "consistent-return": 2, 86 | "spaced-line-comment": 0, 87 | "strict": [2, "global"], 88 | "eqeqeq": 2, 89 | "guard-for-in": 2, 90 | "no-alert": 2, 91 | "no-caller": 2, 92 | "no-labels": 2, 93 | "no-eval": 2, 94 | "no-fallthrough": 2, 95 | "default-case": 2, 96 | "no-iterator": 2, 97 | "no-loop-func": 2, 98 | "no-multi-spaces": 2, 99 | "no-multi-str": 2, 100 | "no-new": 2, 101 | "no-param-reassign": 2, 102 | "no-proto": 2, 103 | "no-redeclare": 2, 104 | "no-return-assign": 2, 105 | "no-self-compare": 2, 106 | "no-sequences": 2, 107 | "no-throw-literal": 2, 108 | "no-unused-expressions": 2, 109 | "no-with": 2, 110 | "vars-on-top": 0, 111 | "wrap-iife": [2, "outside"], 112 | "valid-typeof": 2, 113 | "max-statements": [1, 30], 114 | "max-params": [1, 6], 115 | "no-var": 0, 116 | "no-unexpected-multiline": 2, 117 | "dot-location": [2, "property"], 118 | "no-unreachable": 2, 119 | "no-negated-in-lhs": 2, 120 | "no-irregular-whitespace": 2, 121 | "no-invalid-regexp": 2, 122 | "no-func-assign": 2, 123 | "no-extra-semi": 2, 124 | "no-extra-boolean-cast": 2, 125 | "no-empty": 2, 126 | "no-duplicate-case": 2, 127 | "no-dupe-keys": 2, 128 | "no-dupe-args": 2, 129 | "no-constant-condition": 2, 130 | "comma-style": [2, "last"], 131 | "eol-last": 2, 132 | "no-lonely-if": 2, 133 | "jsx-quotes": ["error", "prefer-single"], 134 | "react/display-name": 1, 135 | "react/jsx-boolean-value": 0, 136 | "react/jsx-no-undef": 2, 137 | "react/jsx-sort-prop-types": 0, 138 | "react/jsx-sort-props": 0, 139 | "react/jsx-uses-react": 1, 140 | "react/jsx-uses-vars": 1, 141 | "react/no-did-mount-set-state": 1, 142 | "react/no-did-update-set-state": 1, 143 | "react/no-multi-comp": 1, 144 | "react/no-unknown-property": 1, 145 | "react/prop-types": 1, 146 | "react/react-in-jsx-scope": 1, 147 | "react/self-closing-comp": 1, 148 | "react/sort-comp": 1, 149 | "react/jsx-wrap-multilines": 1, 150 | "generator-star-spacing": 1, 151 | "babel/new-cap": 0, 152 | "babel/object-curly-spacing": [2, "never"], 153 | "object-shorthand": 1, 154 | "babel/arrow-parens": 0, 155 | "no-await-in-loop": 1 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | [include] 20 | 21 | [libs] 22 | node_modules/react-native/Libraries/react-native/react-native-interface.js 23 | node_modules/react-native/flow/ 24 | 25 | [options] 26 | emoji=true 27 | 28 | module.system=haste 29 | 30 | munge_underscores=true 31 | 32 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 33 | 34 | suppress_type=$FlowIssue 35 | suppress_type=$FlowFixMe 36 | suppress_type=$FlowFixMeProps 37 | suppress_type=$FlowFixMeState 38 | suppress_type=$FixMe 39 | 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 41 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 42 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 43 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 44 | 45 | unsafe.enable_getters_and_setters=true 46 | 47 | [version] 48 | ^0.57.0 49 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.8" 4 | - "7.1" 5 | sudo: false 6 | cache: 7 | directories: 8 | - node_modules 9 | - ios/Pods 10 | - $HOME/.yarn-cache 11 | - $HOME/.gradle/caches/ 12 | - $HOME/.gradle/wrapper/ 13 | env: 14 | - NODE_ENV='test' 15 | script: 16 | - npm test 17 | - npm run bundle:ios 18 | matrix: 19 | include: 20 | - language: android 21 | os: linux 22 | jdk: oraclejdk8 23 | before_cache: 24 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 25 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 26 | sudo: required 27 | node_js: false 28 | before_install: 29 | - nvm install 7 30 | - node --version 31 | - travis_retry curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - 32 | - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 33 | - travis_retry sudo apt-get update -qq 34 | - travis_retry sudo apt-get install -y -qq yarn 35 | install: 36 | - yarn 37 | android: 38 | components: 39 | - tools 40 | - platform-tools 41 | - build-tools-25.0.3 42 | - build-tools-26.0.1 43 | - android-25 44 | - android-26 45 | - extra-android-m2repository 46 | - extra-google-google_play_services 47 | - extra-google-m2repository 48 | - addon-google_apis-google-16 49 | licenses: 50 | - 'android-sdk-preview-license-.+' 51 | - 'android-sdk-license-.+' 52 | - 'google-gdk-license-.+' 53 | script: 54 | - cd android && ./gradlew assembleDebug && ./gradlew assembleRelease 55 | - language: objective-c 56 | os: osx 57 | osx_image: xcode8.3 58 | node_js: false 59 | podfile: ios/Podfile 60 | before_install: 61 | - nvm install 7 62 | - node --version 63 | - travis_retry npm install -g yarn 64 | - yarn -version 65 | - travis_retry yarn 66 | - rvm install ruby-2.2.2 67 | - gem install cocoapods 68 | - gem install xcpretty -N 69 | - brew update 70 | - pod install --project-directory=ios 71 | install: 72 | - travis_retry gem install xcpretty 73 | xcode_project: ios/Nearby.xcworkspace 74 | xcode_scheme: ios/NearbyTests 75 | script: 76 | - cd ios 77 | - xcodebuild -workspace Nearby.xcworkspace -scheme Nearby -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guide 2 | 3 | We welcome all contributions to **Nearby Live** - We advise that you follow this guide to ensure a pleasant and efficient experience. Comments or issues that violate the guidelines mentioned here will be closed, sometimes without warning. 4 | 5 | ## Behaviour 6 | 7 | Please be respectful to others. Any offensive comments or less-than-helpful remarks will be removed. We want to encourage a friendly environment for all to participate in. Any harrassment will be reported and associated accounts blocked. Rude or disrespectful comments to contributors or owners will be dealt with similarly. 8 | 9 | ## Issues 10 | 11 | Please allow some time for issues to be responded to. Buttercup is not our full-time occupation, so it may take some days for us to comment. Creators of issues should state clearly what they're requesting/reporting. 12 | 13 | Issues that are unclear may be closed without notice. 14 | 15 | ### Bugs 16 | 17 | Bug reports should be clear and concise, and sometimes contain the following information: 18 | 19 | * Version of the software the issue occurred in 20 | * Operating system 21 | * Steps to reproduce the problem 22 | * Example archive, if applicable (without sensitive passwords) 23 | * Screenshots of issue 24 | 25 | If a template is used, all items must be filled out. 26 | 27 | ## Contributions 28 | 29 | When adding a feature, it is important to ensure that the direction taken is in line with a previously filed issue. Features should be discussed with the owners before any work is accepted. PRs that add work outside of agreed parameters may be closed without notice. 30 | 31 | Bug fixes are welcome, but are also subject to following the architecture of the application. Updates may be requested to PRs that contain code not befitting the existing quality or structure of the application. 32 | 33 | All PRs must follow linting and style rules, and generally keep to the style and layout of the application. 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 |
5 | 6 | 7 |                   8 | [![React-Native](https://img.shields.io/badge/react--native-0.50.3-green.svg)](https://facebook.github.io/react-native) 9 | [![Build Status](https://travis-ci.org/N3TC4T/Nearby-Live.svg?branch=master)](https://travis-ci.org/N3TC4T/Nearby-Live) 10 | ![Dependencies](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg) 11 | [![GitHub Issues](https://img.shields.io/github/issues/N3TC4T/Nearby-Live.svg)](https://github.com/N3TC4T/Nearby-Live/issues) 12 | ![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg) 13 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 14 | 15 | ## Basic Overview 16 | 17 | An iOS and Android client for http://wnmlive.com/ written in [React Native](https://facebook.github.io/react-native/) with [Redux](http://redux.js.org/). 18 | 19 | 20 | 21 | ## Screenshots 22 |

23 | 24 | 25 | ## Existing functionalities 26 | 27 | - [ ] Authentication 28 | - [x] Sign in ( Facebook, Email) 29 | - [ ] Sign out 30 | - [ ] Stream 31 | - [x] Recent, Following, Trending 32 | - [ ] Filter by Location 33 | - [ ] Post 34 | - [x] Watch Post & Unwatch 35 | - [x] View post detail and comments 36 | - [x] Report objectionable content 37 | - [ ] Publish Post 38 | - [ ] Upload images & GIF 39 | - [ ] Conversation 40 | - [x] Conversations List 41 | - [ ] View Conversation detail 42 | - [ ] Send Image & Voice & Video 43 | - [ ] People 44 | - [ ] Notifications 45 | - [x] View list of notifications 46 | - [ ] Notification alert (Push notification) 47 | - [ ] Profile 48 | - [x] User profile 49 | - [x] User posts 50 | - [ ] User photos 51 | - [x] User gifts 52 | - [ ] Account Settings 53 | - [ ] Profile edit 54 | - [ ] Profile picture 55 | - [ ] Privacy Settings 56 | 57 | 58 | ## Installation 59 | Ensure that you're using **NodeJS 7** or newer on OSX. Android projects can be built and tested on Linux and Windows, but for iOS you need OSX . 60 | 61 | Before getting started, ensure you follow the [official React Native Getting Started guide](https://facebook.github.io/react-native/docs/getting-started.html) for your desired platform (iOS/Android). It is also recommended to have the react-native-cli installed: 62 | 63 | ```shell 64 | yarn global add react-native-cli 65 | ``` 66 | 67 | Run the following to initialise the project: 68 | 69 | ```shell 70 | yarn 71 | ``` 72 | 73 | ### iOS development 74 | Providing Xcode is setup correctly, Follow link bellow for running in Xcode : 75 | 76 | [Running on IOS Devices](http://facebook.github.io/react-native/docs/running-on-device.html#running-your-app-on-ios-devices) 77 | 78 | Also don't forget to install Pod dependencies 79 | ```shell 80 | pod install 81 | ``` 82 | 83 | - for opening project in Xcode use `Nearby.xcworkspace` 84 | 85 | 86 | ### Android development 87 | Ensure that Android Studio is setup correctly and that an AVD has been created. The virtual device must be on **API level 23** or greater running **Android 6.0** or newer. You must have the AVD started before continuing with no other devices connected. To ensure you only have one device running, execute the following on the command-line: 88 | 89 | ```shell 90 | adb devices 91 | ``` 92 | 93 | To run the application in the virtual device, run the following: 94 | 95 | ```shell 96 | yarn start:android 97 | ``` 98 | 99 | #### Running on an Android device 100 | To run on an actual device, first terminate any AVDs that are running. Connect the phone over USB and run `adb devices` to ensure that it shows up. You can then run `yarn start:android` to launch the application on the device. 101 | 102 | The same software version restrictions apply to real devices. 103 | 104 | #### Building an APK 105 | To build a signed APK: 106 | 107 | 1. Close all other development resources for the project. 108 | 2. Uncomment `signingConfigs` section in `android/app/build.gradle` and set params as your .keystore file . 109 | 3. Run `release-debug.sh` (it's gonna build an Signed APK and Install on your device) 110 | 111 | 112 | ## Other Projects 113 | #### Nearby Live Desktop 114 | Nearby Live for OSX and Linux Desktop 115 | ```text 116 | https://github.com/N3TC4T/Nearby-Live-Desktop 117 | ``` 118 | 119 | ## Contributing 120 | Please take a look at our [contributing](https://github.com/N3TC4T/Nearby-Live/blob/master/CONTRIBUTING.md) guidelines if you're interested in helping! 121 | 122 | ### Development 123 | Please keep in-line with the code style of each file, regardless of what tests are run (linting etc.). When creating new files their format is expected to closely resemble that of other existing source files. -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.nearby", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.nearby", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /android/app/src/main/java/com/nearby/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nearby; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | import android.content.Intent; 6 | 7 | public class MainActivity extends ReactActivity { 8 | 9 | @Override 10 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 11 | super.onActivityResult(requestCode, resultCode, data); 12 | MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data); 13 | } 14 | 15 | /** 16 | * Returns the name of the main component registered from JavaScript. 17 | * This is used to schedule rendering of the component. 18 | */ 19 | @Override 20 | protected String getMainComponentName() { 21 | return "Nearby"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/nearby/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.nearby; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.brentvatne.react.ReactVideoPackage; 7 | import com.oblador.vectoricons.VectorIconsPackage; 8 | import com.imagepicker.ImagePickerPackage; 9 | import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage; 10 | import com.RNFetchBlob.RNFetchBlobPackage; 11 | import com.wix.interactable.Interactable; 12 | import com.facebook.react.ReactNativeHost; 13 | import com.facebook.react.ReactPackage; 14 | import com.facebook.react.shell.MainReactPackage; 15 | import com.facebook.soloader.SoLoader; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import com.facebook.CallbackManager; 21 | import com.facebook.FacebookSdk; 22 | import com.facebook.reactnative.androidsdk.FBSDKPackage; 23 | import com.facebook.appevents.AppEventsLogger; 24 | 25 | public class MainApplication extends Application implements ReactApplication { 26 | 27 | private static CallbackManager mCallbackManager = CallbackManager.Factory.create(); 28 | 29 | protected static CallbackManager getCallbackManager() { 30 | return mCallbackManager; 31 | } 32 | 33 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 34 | @Override 35 | public boolean getUseDeveloperSupport() { 36 | return BuildConfig.DEBUG; 37 | } 38 | 39 | @Override 40 | protected List getPackages() { 41 | return Arrays.asList( 42 | new MainReactPackage(), 43 | new FBSDKPackage(mCallbackManager), 44 | new ReactVideoPackage(), 45 | new VectorIconsPackage(), 46 | new ImagePickerPackage(), 47 | new GoogleAnalyticsBridgePackage(), 48 | new RNFetchBlobPackage(), 49 | new Interactable() 50 | ); 51 | } 52 | 53 | @Override 54 | protected String getJSMainModuleName() { 55 | return "index"; 56 | } 57 | }; 58 | 59 | @Override 60 | public ReactNativeHost getReactNativeHost() { 61 | return mReactNativeHost; 62 | } 63 | 64 | @Override 65 | public void onCreate() { 66 | super.onCreate(); 67 | FacebookSdk.sdkInitialize(getApplicationContext()); 68 | AppEventsLogger.activateApp(this); 69 | SoLoader.init(this, /* native exopackage */ false); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/playstore-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/app/src/main/res/playstore-icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Nearby 3 | 153644678059870 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | url 'https://maven.google.com' 21 | } 22 | maven { 23 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 24 | url "$rootDir/../node_modules/react-native/android" 25 | } 26 | } 27 | } 28 | 29 | subprojects { 30 | afterEvaluate {project -> 31 | if (project.hasProperty("android")) { 32 | android { 33 | compileSdkVersion 26 34 | buildToolsVersion '26.0.1' 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Nearby' 2 | include ':react-native-fbsdk' 3 | project(':react-native-fbsdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fbsdk/android') 4 | include ':react-native-video' 5 | project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android') 6 | include ':react-native-vector-icons' 7 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 8 | include ':react-native-image-picker' 9 | project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android') 10 | include ':react-native-google-analytics-bridge' 11 | project(':react-native-google-analytics-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-analytics-bridge/android') 12 | include ':react-native-fetch-blob' 13 | project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android') 14 | include ':react-native-interactable' 15 | project(':react-native-interactable').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-interactable/lib/android') 16 | 17 | include ':app' 18 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nearby", 3 | "displayName": "Nearby" 4 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Load the App component. 3 | * (All the fun stuff happens in "/src/index.js") 4 | */ 5 | 6 | import { AppRegistry } from 'react-native'; 7 | import AppContainer from './src/'; 8 | 9 | AppRegistry.registerComponent('Nearby', () => AppContainer); -------------------------------------------------------------------------------- /ios/Nearby-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /ios/Nearby-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/Nearby.xcodeproj/xcshareddata/xcschemes/Nearby-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /ios/Nearby.xcodeproj/xcshareddata/xcschemes/Nearby.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 107 | 108 | 109 | 110 | 111 | 112 | 118 | 120 | 126 | 127 | 128 | 129 | 131 | 132 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /ios/Nearby.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Nearby/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Nearby/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | #import 16 | 17 | 18 | @implementation AppDelegate 19 | 20 | 21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 22 | { 23 | NSURL *jsCodeLocation; 24 | 25 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 26 | 27 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 28 | moduleName:@"Nearby" 29 | initialProperties:nil 30 | launchOptions:launchOptions]; 31 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 32 | 33 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 34 | UIViewController *rootViewController = [UIViewController new]; 35 | rootViewController.view = rootView; 36 | self.window.rootViewController = rootViewController; 37 | [self.window makeKeyAndVisible]; 38 | return YES; 39 | } 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | [FBSDKAppEvents activateApp]; 42 | } 43 | 44 | - (BOOL)application:(UIApplication *)application 45 | openURL:(NSURL *)url 46 | sourceApplication:(NSString *)sourceApplication 47 | annotation:(id)annotation { 48 | return [[FBSDKApplicationDelegate sharedInstance] application:application 49 | openURL:url 50 | sourceApplication:sourceApplication 51 | annotation:annotation]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ios/Nearby/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/Launch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Launch.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Launch.jpg", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Launch.jpg", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/Launch.imageset/Launch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/Launch.imageset/Launch.jpg -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/iTunesArtwork@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/iTunesArtwork@1x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/iTunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/iTunesArtwork@2x.png -------------------------------------------------------------------------------- /ios/Nearby/Images.xcassets/iTunesArtwork@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/ios/Nearby/Images.xcassets/iTunesArtwork@3x.png -------------------------------------------------------------------------------- /ios/Nearby/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Nearby 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleURLSchemes 27 | 28 | fb153644678059870 29 | 30 | 31 | 32 | FacebookAppID 33 | 153644678059870 34 | FacebookDisplayName 35 | Nearby 36 | LSApplicationQueriesSchemes 37 | 38 | fbapi 39 | fb-messenger-api 40 | fbauth2 41 | fbshareextension 42 | 43 | LSRequiresIPhoneOS 44 | 45 | NSAppTransportSecurity 46 | 47 | NSExceptionDomains 48 | 49 | localhost 50 | 51 | NSExceptionAllowsInsecureHTTPLoads 52 | 53 | 54 | 55 | 56 | NSLocationWhenInUseUsageDescription 57 | 58 | UIAppFonts 59 | 60 | Entypo.ttf 61 | EvilIcons.ttf 62 | FontAwesome.ttf 63 | Foundation.ttf 64 | Ionicons.ttf 65 | MaterialCommunityIcons.ttf 66 | MaterialIcons.ttf 67 | Octicons.ttf 68 | SimpleLineIcons.ttf 69 | Zocial.ttf 70 | Roboto-Bold.ttf 71 | Roboto-Light.ttf 72 | Roboto-Medium.ttf 73 | Roboto-Regular.ttf 74 | 75 | UILaunchStoryboardName 76 | LaunchScreen 77 | UIRequiredDeviceCapabilities 78 | 79 | armv7 80 | 81 | UISupportedInterfaceOrientations 82 | 83 | UIInterfaceOrientationPortrait 84 | UIInterfaceOrientationLandscapeLeft 85 | UIInterfaceOrientationLandscapeRight 86 | 87 | UIViewControllerBasedStatusBarAppearance 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /ios/Nearby/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/NearbyTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/NearbyTests/NearbyTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface NearbyTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation NearbyTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | target 'Nearby' do 4 | pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga' 5 | pod 'React', path: '../node_modules/react-native', :subspecs => [ 6 | 'Core', 7 | 'RCTActionSheet', 8 | 'RCTAnimation', 9 | 'RCTGeolocation', 10 | 'RCTImage', 11 | 'RCTLinkingIOS', 12 | 'RCTNetwork', 13 | 'RCTSettings', 14 | 'RCTText', 15 | 'RCTVibration', 16 | 'RCTWebSocket' 17 | ] 18 | 19 | pod 'react-native-fetch-blob', :path => '../node_modules/react-native-fetch-blob' 20 | pod 'react-native-image-picker', :path => '../node_modules/react-native-image-picker' 21 | pod 'react-native-video', :path => '../node_modules/react-native-video' 22 | pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' 23 | pod 'Interactable', :path => '../node_modules/react-native-interactable' 24 | pod 'FBSDKCoreKit' 25 | pod 'FBSDKLoginKit' 26 | pod 'FBSDKShareKit' 27 | end 28 | 29 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | /* global jest */ 2 | 3 | jest.mock('Linking', () => 4 | ({ 5 | addEventListener: jest.fn(), 6 | removeEventListener: jest.fn(), 7 | openURL: jest.fn(), 8 | canOpenURL: jest.fn(), 9 | getInitialURL: jest.fn(), 10 | }), 11 | ); 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nearby", 3 | "version": "0.4.3", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "start:android": "react-native run-android", 8 | "start:ios": "react-native run-ios", 9 | "bundle:ios": "node ./node_modules/react-native/local-cli/cli.js bundle --platform ios --entry-file index.js --bundle-output ios/Nearby/main.jsbundle --dev=false --verbose", 10 | "bundle:android": "node ./node_modules/react-native/local-cli/cli.js bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res", 11 | "pretest": "./node_modules/.bin/eslint -c .eslintrc --ext .js src", 12 | "test": "jest" 13 | }, 14 | "jest": { 15 | "preset": "react-native", 16 | "setupFiles": [ 17 | "/jest.setup.js" 18 | ], 19 | "transformIgnorePatterns": [ 20 | "node_modules/(?!react-native|apsl-react-native-button,react-native-device-info|react-clone-referenced-element)" 21 | ], 22 | "collectCoverage": true, 23 | "verbose": true 24 | }, 25 | "dependencies": { 26 | "@expo/react-native-action-sheet": "git+https://github.com/expo/react-native-action-sheet.git", 27 | "md5": "^2.2.1", 28 | "moment": "^2.17.1", 29 | "prop-types": "^15.6.0", 30 | "react": "16.0.0", 31 | "react-native": "0.51.0", 32 | "react-native-fbsdk": "^0.6.3", 33 | "react-native-fetch-blob": "^0.10.8", 34 | "react-native-google-analytics-bridge": "^5.3.3", 35 | "react-native-image-picker": "^0.26.7", 36 | "react-native-interactable": "^0.1.7", 37 | "react-native-invertible-scroll-view": "^1.1.0", 38 | "react-native-parsed-text": "0.0.20", 39 | "react-native-progress": "^3.4.0", 40 | "react-native-root-siblings": "^2.2.0", 41 | "react-native-router-flux": "3.43.0", 42 | "react-native-side-menu": "^0.20.1", 43 | "react-native-tab-view": "0.0.70", 44 | "react-native-vector-icons": "^4.4.0", 45 | "react-native-video": "^2.0.0", 46 | "react-redux": "^5.0.6", 47 | "reconnecting-websocket": "^3.0.3", 48 | "redux": "^3.6.0", 49 | "redux-logger": "^3.0.6", 50 | "redux-orm": "^0.9.0-rc.3", 51 | "redux-persist": "^4.4.2", 52 | "redux-thunk": "^2.1.0", 53 | "shallowequal": "1.0.2", 54 | "uuid": "3.1.0" 55 | }, 56 | "devDependencies": { 57 | "babel-core": "^6.17.0", 58 | "babel-eslint": "^8.0.2", 59 | "babel-jest": "^21.2.0", 60 | "babel-plugin-transform-remove-console": "^6.8.4", 61 | "babel-plugin-transform-runtime": "^6.23.0", 62 | "babel-preset-es2015": "^6.18.0", 63 | "babel-preset-react-native": "4.0.0", 64 | "babel-preset-stage-2": "^6.22.0", 65 | "babel-register": "^6.16.3", 66 | "eslint": "^4.11.0", 67 | "eslint-plugin-babel": "^4.1.2", 68 | "eslint-plugin-import": "^2.2.0", 69 | "eslint-plugin-react": "^7.5.1", 70 | "jest": "21.2.1", 71 | "jest-react-native": "^18.0.0", 72 | "react-test-renderer": "16.1.1" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /release-debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "[!] Creating Release APK ... \n" 4 | 5 | cd android && ./gradlew assembleRelease 6 | 7 | if hash adb 2>/dev/null; then 8 | echo -e "\n[!] Installing on android devices with ADB ..." 9 | for line in `adb devices | grep -v "List" | awk '{print $1}'` 10 | do 11 | device=`echo $line | awk '{print $1}'` 12 | echo -e "[+] trying to install on device $device ..." 13 | adb -s $device install -r app/build/outputs/apk/app-release.apk 14 | done 15 | else 16 | echo "\n[!] ADB is not installed , please install it and try again ." 17 | fi -------------------------------------------------------------------------------- /src/assets/font/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/font/Roboto-Bold.ttf -------------------------------------------------------------------------------- /src/assets/font/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/font/Roboto-Light.ttf -------------------------------------------------------------------------------- /src/assets/font/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/font/Roboto-Medium.ttf -------------------------------------------------------------------------------- /src/assets/font/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/font/Roboto-Regular.ttf -------------------------------------------------------------------------------- /src/assets/image/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/image/loading.gif -------------------------------------------------------------------------------- /src/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/image/logo.png -------------------------------------------------------------------------------- /src/assets/image/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@images" 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/image/placeholder.user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/image/placeholder.user.png -------------------------------------------------------------------------------- /src/assets/video/background.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N3TC4T/Nearby-Live/05def0817f074c146bdee30c4ab16b7696ebe165/src/assets/video/background.mp4 -------------------------------------------------------------------------------- /src/components/general/Error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Error Screen 3 | * 4 | 5 | * 6 | */ 7 | import React from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import {View, Image, StyleSheet, TouchableOpacity} from 'react-native'; 10 | 11 | // Consts and Libs 12 | import {AppStyles, AppColors, AppFonts} from '@theme/'; 13 | 14 | // Components 15 | import {Spacer, Text} from '@ui/'; 16 | 17 | const styles = StyleSheet.create({ 18 | buttonTry: { 19 | alignItems: 'center', 20 | justifyContent: 'center', 21 | padding: 5, 22 | height: 30, 23 | borderColor: AppColors.brand.secondary, 24 | borderWidth: 1.3, 25 | borderRadius: 0.5 26 | }, 27 | buttonTryText: { 28 | color: AppColors.textSecondary, 29 | fontFamily: AppFonts.familyBold 30 | } 31 | }); 32 | 33 | /* Component ==================================================================== */ 34 | const Error = ({text, tryAgain}) => ( 35 | 36 | 40 | 41 | 42 | 43 | {text} 44 | 45 | 46 | 47 | {!!tryAgain && 48 | 53 | 54 | TRY AGAIN 55 | 56 | } 57 | 58 | ); 59 | 60 | Error.propTypes = {text: PropTypes.string, tryAgain: PropTypes.func}; 61 | Error.defaultProps = {text: 'Woops, Something went wrong.', tryAgain: null}; 62 | Error.componentName = 'Error'; 63 | 64 | /* Export Component ==================================================================== */ 65 | export default Error; 66 | 67 | -------------------------------------------------------------------------------- /src/components/general/Loading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loading Screen 3 | * 4 | 5 | * 6 | */ 7 | import React, {Component} from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import {View} from 'react-native'; 10 | import ProgressCircle from 'react-native-progress/Circle'; 11 | 12 | // Consts and Libs 13 | import {AppStyles} from '@theme/'; 14 | 15 | // Components 16 | import {Spacer, Text} from '@ui/'; 17 | 18 | /* Component ==================================================================== */ 19 | class Loading extends Component { 20 | static componentName = 'Loading'; 21 | 22 | static propTypes = { 23 | text: PropTypes.string, 24 | transparent: 25 | PropTypes.bool 26 | } 27 | 28 | static defaultProps = { 29 | text: null, 30 | transparent: false 31 | }; 32 | 33 | render = () => { 34 | const {transparent, text} = this.props; 35 | 36 | return ( 37 | 44 | 45 | 46 | 47 | 48 | {!!text && {text}} 49 | 50 | ); 51 | } 52 | } 53 | 54 | /* Export Component ==================================================================== */ 55 | export default Loading; 56 | -------------------------------------------------------------------------------- /src/components/general/Placeholder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Placeholder Scene 3 | * 4 | 5 | * 6 | */ 7 | import React from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import {View} from 'react-native'; 10 | 11 | // Consts and Libs 12 | import {AppStyles} from '@theme/'; 13 | 14 | // Components 15 | import {Text} from '@ui/'; 16 | 17 | /* Component ==================================================================== */ 18 | const Placeholder = ({text}) => ( 19 | 20 | {text} 21 | 22 | ); 23 | 24 | Placeholder.propTypes = {text: PropTypes.string}; 25 | Placeholder.defaultProps = {text: 'placeholder'}; 26 | Placeholder.componentName = 'Placeholder'; 27 | 28 | /* Export Component ==================================================================== */ 29 | export default Placeholder; 30 | -------------------------------------------------------------------------------- /src/components/general/WebView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Web View 3 | * 4 | * 5 | * 6 | */ 7 | import React, {Component} from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import { 10 | WebView, 11 | StyleSheet, 12 | InteractionManager 13 | } from 'react-native'; 14 | 15 | // Consts and Libs 16 | import {AppColors, AppStyles} from '@theme/'; 17 | 18 | // Components 19 | import Loading from '@components/general/Loading'; 20 | import Error from '@components/general/Error'; 21 | 22 | /* Styles ==================================================================== */ 23 | const styles = StyleSheet.create({ 24 | container: { 25 | backgroundColor: AppColors.background 26 | } 27 | }); 28 | 29 | /* Component ==================================================================== */ 30 | class AppWebView extends Component { 31 | static componentName = 'AppWebView'; 32 | 33 | static propTypes = { 34 | url: PropTypes.string.isRequired, 35 | onNavigationStateChange: PropTypes.func 36 | } 37 | 38 | static defaultProps = { 39 | onNavigationStateChange: null 40 | } 41 | 42 | constructor(props) { 43 | super(props); 44 | 45 | this.state = { 46 | loading: true, 47 | webViewURL: props.url || null 48 | }; 49 | } 50 | 51 | componentDidMount = () => { 52 | // Wait until interaction has finished before loading the webview in 53 | InteractionManager.runAfterInteractions(() => { 54 | this.setState({loading: false}); 55 | }); 56 | } 57 | 58 | /** 59 | * Each time page loads, update the URL 60 | */ 61 | onNavigationStateChange = (navState) => { 62 | this.setState({webViewURL: navState.url}); 63 | if (this.props.onNavigationStateChange) {this.props.onNavigationStateChange(navState.url);} 64 | } 65 | 66 | render = () => { 67 | const {webViewURL, loading} = this.state; 68 | 69 | if (loading) {return ;} 70 | if (!webViewURL) {return ;} 71 | 72 | return ( 73 | 81 | ); 82 | } 83 | } 84 | 85 | /* Export Component ==================================================================== */ 86 | export default AppWebView; 87 | -------------------------------------------------------------------------------- /src/components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@components" 3 | } 4 | -------------------------------------------------------------------------------- /src/components/ui/Avatar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | Animated, 4 | View, 5 | Image, 6 | TouchableWithoutFeedback 7 | } from 'react-native'; 8 | import PropTypes from 'prop-types'; 9 | 10 | export default class AvatarImage extends Component { 11 | static propTypes = { 12 | source: Image.propTypes.source, 13 | imgKey: PropTypes.string, 14 | size: PropTypes.number, 15 | onPress: PropTypes.func 16 | }; 17 | 18 | static defaultProps = { 19 | size: 36, 20 | imgKey: null, 21 | onPress: null 22 | }; 23 | 24 | constructor(props) { 25 | super(props); 26 | 27 | this.state = { 28 | thumbnailOpacity: new Animated.Value(0), 29 | completelyLoaded: false 30 | }; 31 | } 32 | onLoad() { 33 | Animated.timing(this.state.thumbnailOpacity, { 34 | toValue: 0, 35 | duration: 250 36 | }).start(); 37 | 38 | this.setState({completelyLoaded: true}); 39 | } 40 | onThumbnailLoad() { 41 | Animated.timing(this.state.thumbnailOpacity, { 42 | toValue: 1, 43 | duration: 250 44 | }).start(); 45 | } 46 | 47 | render() { 48 | const {completelyLoaded} = this.state; 49 | 50 | return ( 51 | 53 | 54 | this.onLoad(event)} 64 | /> 65 | 66 | { !completelyLoaded && 67 | 68 | this.onThumbnailLoad(event)} 81 | /> 82 | } 83 | 84 | 85 | 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/components/ui/BackButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BackButton 3 | * 4 | 5 | * 6 | */ 7 | import React from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import {TouchableOpacity} from 'react-native'; 10 | import {Icon} from '@components/ui'; 11 | 12 | import {Actions} from 'react-native-router-flux'; 13 | 14 | /* Component ==================================================================== */ 15 | const BackButton = ({size, color}) => ( 16 | 17 | 18 | 19 | ); 20 | 21 | BackButton.propTypes = {size: PropTypes.number, color: PropTypes.string}; 22 | BackButton.defaultProps = {size: 20, color: '#232F3A'}; 23 | BackButton.componentName = 'BackButton'; 24 | 25 | /* Export Component ==================================================================== */ 26 | export default BackButton; 27 | -------------------------------------------------------------------------------- /src/components/ui/Badge.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Badge - type 3 | * 4 | 5 | * 6 | */ 7 | 8 | import React from 'react'; 9 | import { 10 | View, 11 | StyleSheet 12 | } from 'react-native'; 13 | 14 | import PropTypes from 'prop-types'; 15 | 16 | // Components 17 | import {Text} from '@ui/'; 18 | 19 | const styles = StyleSheet.create({ 20 | badge: { 21 | height: 15, 22 | width: 37, 23 | top: 3, 24 | marginLeft: 5, 25 | borderRadius: 20 26 | }, 27 | badgeGold: { 28 | backgroundColor: '#F9BA32' 29 | }, 30 | badgeVip: { 31 | backgroundColor: '#006C84' 32 | }, 33 | badgeStaff: { 34 | backgroundColor: '#E7472E' 35 | }, 36 | badgeAdmin: { 37 | backgroundColor: '#C8000A' 38 | }, 39 | badgeOwner: { 40 | backgroundColor: '#00b7d3', 41 | height: 10, 42 | width: 30 43 | }, 44 | text: { 45 | lineHeight: 14, 46 | fontSize: 10, 47 | color: 'white', 48 | textAlign: 'center' 49 | } 50 | }); 51 | 52 | const styleSelector = (type) => { 53 | switch (type) { 54 | case 'gold': 55 | return styles.badgeGold; 56 | case 'vip staff': 57 | case 'staff': 58 | return styles.badgeStaff; 59 | case 'vip admin': 60 | case 'admin': 61 | return styles.badgeAdmin; 62 | case 'vip': 63 | return styles.badgeVip; 64 | case 'owner': 65 | return styles.badgeOwner; 66 | default: 67 | break; 68 | } 69 | 70 | return null; 71 | }; 72 | 73 | const textSelector = (type) => { 74 | switch (type) { 75 | case 'gold': 76 | return 'GOLD'; 77 | case 'vip staff': 78 | case 'staff': 79 | return 'STAFF'; 80 | case 'vip admin': 81 | case 'admin': 82 | return 'ADMIN'; 83 | case 'vip': 84 | return 'VIP'; 85 | case 'owner': 86 | return 'Owner'; 87 | default: 88 | break; 89 | } 90 | 91 | return null; 92 | }; 93 | 94 | const Badge = ({type}) => ( 95 | 96 | 97 | {textSelector(type)} 98 | 99 | 100 | ); 101 | 102 | Badge.propTypes = { 103 | type: PropTypes.string 104 | }; 105 | 106 | Badge.defaultProps = { 107 | type: '' 108 | }; 109 | 110 | Badge.componentName = 'Badge'; 111 | 112 | /* Export Component ==================================================================== */ 113 | export default Badge; 114 | -------------------------------------------------------------------------------- /src/components/ui/Icon.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { 4 | Platform, 5 | TouchableHighlight, 6 | View, 7 | StyleSheet, 8 | ViewPropTypes, 9 | Text as NativeText 10 | } from 'react-native'; 11 | 12 | import ZocialIcon from 'react-native-vector-icons/Zocial'; 13 | import OcticonIcon from 'react-native-vector-icons/Octicons'; 14 | import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; 15 | import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons'; 16 | import Ionicon from 'react-native-vector-icons/Ionicons'; 17 | import FoundationIcon from 'react-native-vector-icons/Foundation'; 18 | import EvilIcon from 'react-native-vector-icons/EvilIcons'; 19 | import EntypoIcon from 'react-native-vector-icons/Entypo'; 20 | import FAIcon from 'react-native-vector-icons/FontAwesome'; 21 | import SimpleLineIcon from 'react-native-vector-icons/SimpleLineIcons'; 22 | 23 | const getIconType = (type) => { 24 | switch (type) { 25 | case 'zocial': 26 | return ZocialIcon; 27 | case 'octicon': 28 | return OcticonIcon; 29 | case 'material': 30 | return MaterialIcon; 31 | case 'material-community': 32 | return MaterialCommunityIcon; 33 | case 'ionicon': 34 | return Ionicon; 35 | case 'foundation': 36 | return FoundationIcon; 37 | case 'evilicon': 38 | return EvilIcon; 39 | case 'entypo': 40 | return EntypoIcon; 41 | case 'font-awesome': 42 | return FAIcon; 43 | case 'simple-line-icon': 44 | return SimpleLineIcon; 45 | default: 46 | return MaterialIcon; 47 | } 48 | }; 49 | 50 | const styles = StyleSheet.create({ 51 | button: { 52 | margin: 7 53 | }, 54 | raised: { 55 | ...Platform.select({ 56 | ios: { 57 | shadowColor: 'rgba(0,0,0, .4)', 58 | shadowOffset: {height: 1, width: 1}, 59 | shadowOpacity: 1, 60 | shadowRadius: 1 61 | }, 62 | android: { 63 | elevation: 2 64 | } 65 | }) 66 | } 67 | }); 68 | 69 | const Icon = (props) => { 70 | const { 71 | type, 72 | name, 73 | size, 74 | color, 75 | iconStyle, 76 | component, 77 | underlayColor, 78 | reverse, 79 | raised, 80 | containerStyle, 81 | reverseColor, 82 | onPress, 83 | ...attributes 84 | } = props; 85 | 86 | let Component = View; 87 | if (onPress) { 88 | Component = TouchableHighlight; 89 | } 90 | if (component) { 91 | Component = component; 92 | } 93 | 94 | // Todo: Fix me 95 | // eslint-disable-next-line no-shadow 96 | const Icon = getIconType(type); 97 | 98 | return ( 99 | 120 | 126 | 127 | ); 128 | }; 129 | 130 | Icon.propTypes = { 131 | type: PropTypes.string, 132 | name: PropTypes.string.isRequired, 133 | size: PropTypes.number, 134 | color: PropTypes.string, 135 | component: PropTypes.element, 136 | underlayColor: PropTypes.string, 137 | reverse: PropTypes.bool, 138 | raised: PropTypes.bool, 139 | containerStyle: ViewPropTypes.style, 140 | iconStyle: NativeText.propTypes.style, 141 | onPress: PropTypes.func, 142 | reverseColor: PropTypes.string 143 | }; 144 | 145 | Icon.defaultProps = { 146 | type: 'material', 147 | underlayColor: 'white', 148 | reverse: false, 149 | raised: false, 150 | size: 24, 151 | color: 'black', 152 | reverseColor: 'white', 153 | component: null, 154 | containerStyle: null, 155 | iconStyle: null, 156 | onPress: null 157 | }; 158 | 159 | export default Icon; 160 | -------------------------------------------------------------------------------- /src/components/ui/Pulse.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, StyleSheet, Animated, Easing, Dimensions} from 'react-native'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const {height, width} = Dimensions.get('window'); 6 | 7 | class Pulse extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | this.anim = new Animated.Value(0); 12 | } 13 | 14 | componentDidMount() { 15 | Animated.timing(this.anim, { 16 | toValue: 1, 17 | duration: this.props.interval, 18 | easing: Easing.in 19 | }) 20 | .start(); 21 | } 22 | 23 | render() { 24 | const { 25 | size, pulseMaxSize, borderColor, backgroundColor, getStyle 26 | } = this.props; 27 | 28 | return ( 29 | 36 | 55 | 56 | ); 57 | } 58 | } 59 | 60 | Pulse.propTypes = { 61 | interval: PropTypes.number, 62 | size: PropTypes.number, 63 | pulseMaxSize: PropTypes.number, 64 | borderColor: PropTypes.string, 65 | backgroundColor: PropTypes.string, 66 | getStyle: PropTypes.func 67 | }; 68 | 69 | Pulse.defaultProps = { 70 | interval: 2000, 71 | size: 100, 72 | pulseMaxSize: 250, 73 | borderColor: '#CB0000', 74 | backgroundColor: '#ED225B55', 75 | getStyle: null 76 | }; 77 | 78 | const styles = StyleSheet.create({ 79 | circleWrapper: { 80 | justifyContent: 'center', 81 | alignItems: 'center', 82 | position: 'absolute', 83 | left: width / 2, 84 | top: height / 2 85 | }, 86 | circle: { 87 | borderWidth: 4 * StyleSheet.hairlineWidth 88 | } 89 | }); 90 | 91 | /* Export Component ==================================================================== */ 92 | export default Pulse; 93 | -------------------------------------------------------------------------------- /src/components/ui/Search.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | StyleSheet, 4 | TextInput, 5 | View 6 | } from 'react-native'; 7 | import PropTypes from 'prop-types'; 8 | 9 | // Consts and Libs 10 | import {AppStyles} from '@theme/'; 11 | 12 | // Components 13 | import {Icon} from '@components/ui'; 14 | 15 | export default class Search extends Component { 16 | constructor() { 17 | super(); 18 | this.state = { 19 | text: '' 20 | }; 21 | } 22 | render() { 23 | const { 24 | placeHolder, backgroundColor, innerBackground, border, radius, onChangeText 25 | } = this.props; 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | { this.setState({text}); onChangeText(text); }} 35 | value={this.state.text} 36 | placeholder={placeHolder} 37 | placeholderTextColor='#9197A3' 38 | underlineColorAndroid='rgba(0,0,0,0)' 39 | /> 40 | 41 | 42 | ); 43 | } 44 | } 45 | Search.defaultProps = { 46 | placeHolder: 'Search messages', 47 | backgroundColor: '#FFFFFF', 48 | // backgroundColor: primary, 49 | innerBackground: '#FFFFFF', 50 | radius: 5, 51 | border: false, 52 | onChangeText: null 53 | }; 54 | Search.propTypes = { 55 | onChangeText: PropTypes.func, 56 | placeHolder: PropTypes.string, 57 | backgroundColor: PropTypes.string, 58 | innerBackground: PropTypes.string, 59 | radius: PropTypes.number, 60 | borderColor: PropTypes.string, 61 | border: PropTypes.bool, 62 | iconColor: PropTypes.string 63 | }; 64 | 65 | const styles = StyleSheet.create({ 66 | input: { 67 | height: 40, 68 | paddingHorizontal: 10, 69 | paddingRight: 30 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /src/components/ui/SegmentButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { 4 | View, 5 | Text as NativeText, 6 | StyleSheet, 7 | ViewPropTypes, 8 | TouchableHighlight, 9 | Platform 10 | } from 'react-native'; 11 | 12 | import {Text} from '@ui/'; 13 | import {AppColors} from '@theme/'; 14 | 15 | const ButtonGroup = (props) => { 16 | const { 17 | component, 18 | buttons, 19 | onPress, 20 | selectedIndex, 21 | containerStyle, 22 | innerBorderStyle, 23 | lastBorderStyle, 24 | buttonStyle, 25 | textStyle, 26 | selectedTextStyle, 27 | selectedBackgroundColor, 28 | underlayColor, 29 | activeOpacity, 30 | onHideUnderlay, 31 | onShowUnderlay, 32 | setOpacityTo, 33 | containerBorderRadius, 34 | ...attributes 35 | } = props; 36 | 37 | const Component = component || TouchableHighlight; 38 | return ( 39 | 43 | {buttons.map((button, i) => ( 44 | onPress(i) : () => {}} 51 | key={i} 52 | style={[ 53 | styles.button, 54 | i < buttons.length - 1 && { 55 | borderRightWidth: (innerBorderStyle && 56 | innerBorderStyle.width) || 57 | 1, 58 | borderRightColor: (innerBorderStyle && 59 | innerBorderStyle.color) || 60 | AppColors.segmentButton.borderColor 61 | }, 62 | i === buttons.length - 1 && { 63 | ...lastBorderStyle, 64 | borderTopRightRadius: containerBorderRadius || 3, 65 | borderBottomRightRadius: containerBorderRadius || 3 66 | }, 67 | i === 0 && { 68 | borderTopLeftRadius: containerBorderRadius || 3, 69 | borderBottomLeftRadius: containerBorderRadius || 3 70 | }, 71 | selectedIndex === i && { 72 | backgroundColor: selectedBackgroundColor || AppColors.segmentButton.selectedBackground 73 | } 74 | ]} 75 | > 76 | 77 | {button.element 78 | ? 79 | : 87 | {button} 88 | } 89 | 90 | 91 | ))} 92 | 93 | ); 94 | }; 95 | 96 | const styles = StyleSheet.create({ 97 | button: { 98 | flex: 1 99 | }, 100 | textContainer: { 101 | flex: 1, 102 | justifyContent: 'center', 103 | alignItems: 'center' 104 | }, 105 | container: { 106 | marginLeft: 10, 107 | marginRight: 10, 108 | marginBottom: 5, 109 | marginTop: 5, 110 | borderColor: AppColors.segmentButton.borderColor, 111 | borderWidth: 1, 112 | flexDirection: 'row', 113 | borderRadius: 3, 114 | overflow: 'hidden', 115 | backgroundColor: AppColors.segmentButton.background, 116 | height: 25 117 | }, 118 | buttonText: { 119 | fontSize: 11, 120 | color: AppColors.segmentButton.textColor, 121 | ...Platform.select({ 122 | ios: { 123 | fontWeight: '500' 124 | } 125 | }) 126 | } 127 | }); 128 | 129 | ButtonGroup.propTypes = { 130 | button: PropTypes.object, 131 | component: PropTypes.any, 132 | onPress: PropTypes.func, 133 | buttons: PropTypes.array, 134 | containerStyle: ViewPropTypes.style, 135 | textStyle: NativeText.propTypes.style, 136 | selectedTextStyle: NativeText.propTypes.style, 137 | underlayColor: PropTypes.string, 138 | selectedIndex: PropTypes.number, 139 | activeOpacity: PropTypes.number, 140 | onHideUnderlay: PropTypes.func, 141 | onShowUnderlay: PropTypes.func, 142 | setOpacityTo: PropTypes.any, 143 | innerBorderStyle: PropTypes.shape({ 144 | color: PropTypes.string, 145 | width: PropTypes.number 146 | }), 147 | lastBorderStyle: PropTypes.oneOfType([ 148 | ViewPropTypes.style, 149 | NativeText.propTypes.style 150 | ]), 151 | buttonStyle: ViewPropTypes.style, 152 | selectedBackgroundColor: PropTypes.string, 153 | containerBorderRadius: PropTypes.number 154 | }; 155 | 156 | export default ButtonGroup; 157 | -------------------------------------------------------------------------------- /src/components/ui/Spacer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Spacer 3 | * 4 | 5 | * 6 | */ 7 | import React from 'react'; 8 | import PropTypes from 'prop-types'; 9 | 10 | import {View} from 'react-native'; 11 | 12 | /* Component ==================================================================== */ 13 | const Spacer = ({size}) => ( 14 | 22 | ); 23 | 24 | Spacer.propTypes = {size: PropTypes.number}; 25 | Spacer.defaultProps = {size: 10}; 26 | Spacer.componentName = 'Spacer'; 27 | 28 | /* Export Component ==================================================================== */ 29 | export default Spacer; 30 | -------------------------------------------------------------------------------- /src/components/ui/TabIcon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tabbar Icon 3 | * 4 | 5 | * 6 | */ 7 | import {connect} from 'react-redux'; 8 | 9 | import React, {Component} from 'react'; 10 | 11 | import PropTypes from 'prop-types'; 12 | 13 | import { 14 | StyleSheet, 15 | Text, 16 | View 17 | } from 'react-native'; 18 | 19 | import {Icon} from '@ui/'; 20 | import {AppColors} from '@theme/'; 21 | 22 | /* Styles ==================================================================== */ 23 | const styles = StyleSheet.create({ 24 | IconBadge: { 25 | position: 'absolute', 26 | top: -4, 27 | right: 6, 28 | width: 15, 29 | height: 15, 30 | borderRadius: 15, 31 | alignItems: 'center', 32 | justifyContent: 'center', 33 | backgroundColor: '#FF0000' 34 | } 35 | }); 36 | 37 | /* Pass state to component ==================================================================== */ 38 | const mapStateToProps = (state, props) => ({ 39 | UnreadNotifyCount: props.tabType === 'notification-system' ? state.systemNotifications.UnreadCount : 0 40 | }); 41 | 42 | class TabIcon extends Component { 43 | static propTypes = { 44 | title: PropTypes.string, 45 | icon: PropTypes.string.isRequired, 46 | size: PropTypes.number, 47 | type: PropTypes.string, 48 | selected: PropTypes.bool, 49 | tabType: PropTypes.string, 50 | raised: PropTypes.bool, 51 | UnreadNotifyCount: PropTypes.number.isRequired 52 | } 53 | 54 | static defaultProps = { 55 | icon: 'search', 56 | selected: false, 57 | size: 26, 58 | tabType: '', 59 | raised: false 60 | }; 61 | 62 | render() { 63 | const { 64 | title, icon, size, type, selected, raised 65 | } = this.props; 66 | 67 | return ( 68 | 69 | 82 | 83 | { title } 84 | 85 | {this.props.UnreadNotifyCount > 0 && 86 | 87 | 88 | { this.props.UnreadNotifyCount > 100 89 | ? 'N' 90 | : this.props.UnreadNotifyCount 91 | } 92 | 93 | 94 | } 95 | 96 | ); 97 | } 98 | } 99 | 100 | export default connect(mapStateToProps)(TabIcon); 101 | -------------------------------------------------------------------------------- /src/components/ui/Text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Text 3 | * 4 | Hello World 5 | * 6 | */ 7 | import React, {Component} from 'react'; 8 | import PropTypes from 'prop-types'; 9 | 10 | import {Text} from 'react-native'; 11 | 12 | // Consts and Libs 13 | import {AppStyles} from '@theme/'; 14 | 15 | /* Component ==================================================================== */ 16 | class CustomText extends Component { 17 | static propTypes = { 18 | h1: PropTypes.bool, 19 | h2: PropTypes.bool, 20 | h3: PropTypes.bool, 21 | h4: PropTypes.bool, 22 | h5: PropTypes.bool, 23 | p: PropTypes.bool, 24 | onPress: PropTypes.func, 25 | style: PropTypes.oneOfType([ 26 | PropTypes.array, 27 | PropTypes.shape({}) 28 | ]), 29 | children: PropTypes.node 30 | } 31 | 32 | static defaultProps = { 33 | h1: false, 34 | h2: false, 35 | h3: false, 36 | h4: false, 37 | h5: false, 38 | p: false, 39 | onPress: null, 40 | style: null, 41 | children: null 42 | } 43 | 44 | textProps = () => { 45 | // Defaults 46 | const props = { 47 | ...this.props, 48 | style: [AppStyles.baseText] 49 | }; 50 | 51 | if (this.props.p) {props.style = [AppStyles.p];} 52 | if (this.props.h1) {props.style = [AppStyles.h1];} 53 | if (this.props.h2) {props.style = [AppStyles.h2];} 54 | if (this.props.h3) {props.style = [AppStyles.h3];} 55 | if (this.props.h4) {props.style = [AppStyles.h4];} 56 | if (this.props.h5) {props.style = [AppStyles.h5];} 57 | 58 | if (this.props.style) { 59 | props.style.push(this.props.style); 60 | } 61 | 62 | return props; 63 | } 64 | 65 | render = () => {this.props.children}; 66 | } 67 | 68 | /* Export Component ==================================================================== */ 69 | export default CustomText; 70 | -------------------------------------------------------------------------------- /src/components/ui/alerts/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Alert and Toast 3 | */ 4 | 5 | import Alert from './Alert'; 6 | import Toast from './Toast'; 7 | 8 | export {Alert, Toast}; 9 | -------------------------------------------------------------------------------- /src/components/ui/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UI Elements 3 | */ 4 | 5 | import Icon from './Icon'; 6 | import SegmentButton from './SegmentButton'; 7 | import SearchBar from './Search'; 8 | import Badge from './Badge'; 9 | import Pulse from './Pulse'; 10 | import Spacer from './Spacer'; 11 | import TabIcon from './TabIcon'; 12 | import Image from './Image'; 13 | import Avatar from './Avatar'; 14 | import BackButton from './BackButton'; 15 | import Text from './Text'; 16 | import PostCard from './PostCard'; 17 | 18 | export {Icon, SegmentButton, SearchBar, Badge, Pulse, Image, Avatar, BackButton, Spacer, TabIcon, Text, PostCard}; 19 | -------------------------------------------------------------------------------- /src/components/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ui" 3 | } 4 | -------------------------------------------------------------------------------- /src/constants/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * API Config 3 | */ 4 | 5 | export default { 6 | // The API URL we're connecting to 7 | apiUrl: 'https://www.wnmlive.com/api', 8 | 9 | // Map short names to the actual endpoints, so that we can 10 | // use them like so: AppAPI.ENDPOINT_NAME.METHOD() 11 | // NOTE: They should start with a / 12 | // eg. 13 | // - AppAPI.posts.get() 14 | // - AppAPI.post.post() 15 | // - AppAPI.post.delete() 16 | endpoints: new Map([ 17 | ['token', '/token'], 18 | ['oauth', '/external/token'], 19 | ['connect', '/account/connect'], 20 | 21 | ['stream', '/stream/world/{section}'], 22 | ['livestream', '/livestream/{pid}/{section}'], 23 | 24 | ['conversations', '/conversations'], 25 | 26 | ['notifications', '/system-messages'], 27 | 28 | ['people', '/people'], 29 | ['people_info', '/people/{pid}/{section}'], 30 | 31 | ['upload_image', '/upload-image.ashx'] 32 | ]), 33 | 34 | tokenKey: 'token' 35 | }; 36 | -------------------------------------------------------------------------------- /src/constants/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Global App Config 3 | */ 4 | /* global __DEV__ */ 5 | import {AppColors, AppStyles} from '@theme/'; 6 | 7 | import {Platform} from 'react-native'; 8 | 9 | export default { 10 | // App Details 11 | appName: 'Nearby Live', 12 | 13 | // Build Configuration - eg. Debug or Release? 14 | DEV: __DEV__, 15 | 16 | // Google Analytics - uses a 'dev' account while we're testing 17 | gaTrackingId: (__DEV__) ? 'UA-91031172-1' : 'UA-91031172-1', 18 | 19 | // URLs 20 | urls: { 21 | acceptableUse: 'https://www.wnmlive.com/legal/acceptable-use', 22 | imageCDN: 'https://nearby-images.azureedge.net/image/', 23 | baseURL: 'https://www.wnmlive.com/' 24 | }, 25 | 26 | // Navbar Props 27 | navbarProps: { 28 | hideNavBar: false, 29 | sceneStyle: { 30 | backgroundColor: AppColors.background, 31 | paddingTop: Platform.os === 'ios' ? 64 : 48 32 | }, 33 | navigationBarStyle: { 34 | ...AppStyles.navbar 35 | }, 36 | titleStyle: { 37 | ...AppStyles.navbarTitle 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/constants/errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Error Messages 3 | */ 4 | 5 | export default { 6 | // Defaults 7 | default: 'Hmm, an unknown error occured', 8 | timeout: 'Server Timed Out. Check your internet connection', 9 | invalidJson: 'Response returned is not valid JSON', 10 | notImplemented: 'Not Implemented Yet!', 11 | 12 | // Stream 13 | post404: 'Post not found.', 14 | posts404: 'There are no posts to show.', 15 | noComment: 'No comment yet', 16 | missingStreamSection: 'Missing Stream section', 17 | missingPostID: 'Missing post id', 18 | 19 | // Conversations 20 | missingConversationID: 'Missing conversation id', 21 | 22 | // Notifications 23 | notifications404: 'There are no notification to show.' 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Constants 3 | */ 4 | 5 | import AppConfig from './config'; 6 | import APIConfig from './api'; 7 | import ErrorMessages from './errors'; 8 | 9 | export {AppConfig, APIConfig, ErrorMessages}; 10 | -------------------------------------------------------------------------------- /src/constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@constants" 3 | } 4 | -------------------------------------------------------------------------------- /src/containers/auth/AuthenticateContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Authenticate Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as UserActions from '@redux/core/user/actions'; 8 | 9 | import AuthenticateRender from './AuthenticateView'; 10 | 11 | const mapStateToProps = state => ({ 12 | user: state.user 13 | }); 14 | 15 | const mapDispatchToProps = { 16 | emailLogin: UserActions.emailLogin, 17 | facebookLogin: UserActions.facebookLogin 18 | }; 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(AuthenticateRender); 21 | -------------------------------------------------------------------------------- /src/containers/auth/WebView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Auth WebView 3 | * - Used by sign up and password reset 4 | */ 5 | import React, {Component} from 'react'; 6 | import PropTypes from 'prop-types'; 7 | import {Actions} from 'react-native-router-flux'; 8 | 9 | // Components 10 | import WebView from '@components/general/WebView'; 11 | 12 | let timeout; 13 | 14 | /* Component ==================================================================== */ 15 | class AuthWebView extends Component { 16 | static componentName = 'AuthWebView'; 17 | 18 | static propTypes = { 19 | url: PropTypes.string.isRequired 20 | } 21 | 22 | componentWillUnmount = () => { 23 | // Clear the timeout, otherwise we'll get warnings 24 | // when the user unmounts mid-way timeout 25 | clearTimeout(timeout); 26 | } 27 | 28 | /** 29 | * Pop back a scene when URL changes 30 | * + for when the action is completed 31 | * + restricting people from freely browsing 32 | */ 33 | urlChanged = (newUrl) => { 34 | if (newUrl !== this.props.url) { 35 | timeout = setTimeout(() => { 36 | clearTimeout(timeout); 37 | Actions.pop(); 38 | }, 2000); 39 | } 40 | } 41 | 42 | render = () => ( 43 | 47 | ) 48 | } 49 | 50 | /* Export Component ==================================================================== */ 51 | export default AuthWebView; 52 | -------------------------------------------------------------------------------- /src/containers/launch/LaunchContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Launch Screen Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as UserActions from '@redux/core/user/actions'; 8 | 9 | // The component we're mapping to 10 | import AppLaunchRender from './LaunchView'; 11 | 12 | // What data from the store shall we send to the component? 13 | const mapStateToProps = () => ({ 14 | }); 15 | 16 | // Any actions to map to the component? 17 | const mapDispatchToProps = { 18 | getLoginStatus: UserActions.getLoginStatus 19 | }; 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(AppLaunchRender); 22 | -------------------------------------------------------------------------------- /src/containers/launch/LaunchView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Launch Screen 3 | * - Shows a nice loading screen whilst: 4 | * - Checking if user is logged in, and redirects from there 5 | * - if logged in connect to websocket 6 | */ 7 | import React, {Component} from 'react'; 8 | import PropTypes from 'prop-types'; 9 | 10 | import { 11 | View, 12 | Image, 13 | StatusBar, 14 | StyleSheet 15 | } from 'react-native'; 16 | 17 | import {Actions} from 'react-native-router-flux'; 18 | 19 | // Consts and Libs 20 | import {AppStyles, AppSizes} from '@theme/'; 21 | 22 | /* Styles ==================================================================== */ 23 | const styles = StyleSheet.create({ 24 | logo: { 25 | width: AppSizes.screen.width * 0.55, 26 | resizeMode: 'contain' 27 | }, 28 | loadingText: { 29 | color: '#fff' 30 | } 31 | }); 32 | 33 | /* Component ==================================================================== */ 34 | class AppLaunch extends Component { 35 | static componentName = 'AppLaunch'; 36 | 37 | static propTypes = { 38 | getLoginStatus: PropTypes.func.isRequired 39 | }; 40 | 41 | componentDidMount() { 42 | // Show status bar on app launch 43 | StatusBar.setHidden(false, true); 44 | 45 | // Try to authenticate based on existing token 46 | this.props.getLoginStatus() 47 | // Logged in, show index screen 48 | .then((token) => { 49 | Actions.app({type: 'reset'}); 50 | // connect to websocket 51 | Actions.app({type: 'CONNECT', token}); 52 | }) 53 | // Not Logged in, show Login screen 54 | .catch(() => Actions.authenticate({type: 'reset'})); 55 | } 56 | 57 | render = () => ( 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | ); 69 | } 70 | 71 | /* Export Component ==================================================================== */ 72 | export default AppLaunch; 73 | -------------------------------------------------------------------------------- /src/containers/main/home/HomeContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Home Tabs Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // The component we're mapping to 7 | import HomeTabsRender from './HomeView'; 8 | 9 | // What data from the store shall we send to the component? 10 | const mapStateToProps = state => ({ 11 | UnreadMessagesCount: state.conversations.UnreadCount, 12 | UnseenWatchedCount: state.stream.UnreadCount 13 | }); 14 | 15 | // Any actions to map to the component? 16 | const mapDispatchToProps = { 17 | }; 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(HomeTabsRender); 20 | -------------------------------------------------------------------------------- /src/containers/main/home/conversations/ConversationsListingContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Conversations Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as ConversationsActions from '@redux/core/conversations/actions'; 8 | 9 | // Selectors 10 | import {conversationsSelector} from '@redux/core/conversations/selectors'; 11 | 12 | // Render 13 | import ConversationsListingRender from './ConversationsListingView'; 14 | 15 | /* Redux ==================================================================== */ 16 | const mapStateToProps = state => ({ 17 | conversationsListing: conversationsSelector(state) 18 | }); 19 | 20 | const mapDispatchToProps = { 21 | getConversations: ConversationsActions.getConversations, 22 | uninitializeConversation: ConversationsActions.uninitializeConversation, 23 | deleteConversation: ConversationsActions.deleteConversation, 24 | deleteConversations: ConversationsActions.deleteConversations 25 | }; 26 | 27 | export default connect(mapStateToProps, mapDispatchToProps)(ConversationsListingRender); 28 | -------------------------------------------------------------------------------- /src/containers/main/home/conversations/conversation/ConversationContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Individual Recipe Card Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | import * as ConversationsActions from '@redux/core/conversations/actions'; 7 | import {conversationSelector} from '@redux/core/conversations/selectors'; 8 | 9 | // Components 10 | import ConversationView from './ConversationView'; 11 | 12 | /* Redux ==================================================================== */ 13 | const mapStateToProps = (state, ownProps) => ({ 14 | user: state.user, 15 | conversation: conversationSelector(state, ownProps) 16 | }); 17 | 18 | const mapDispatchToProps = { 19 | getMessages: ConversationsActions.getMessages, 20 | sendMessage: ConversationsActions.sendMessage 21 | }; 22 | 23 | const mergeProps = (stateProps, dispatchProps) => ({ 24 | user: stateProps.user, 25 | conversation: stateProps.conversation, 26 | getMessages: () => dispatchProps.getMessages(stateProps.conversation.lessParams), 27 | sendMessage: dispatchProps.sendMessage 28 | }); 29 | 30 | /* Export Component ==================================================================== */ 31 | export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ConversationView); 32 | -------------------------------------------------------------------------------- /src/containers/main/home/conversations/conversation/ConversationView.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Consts and Libs 5 | import {ErrorMessages} from '@constants/'; 6 | import Error from '@components/general/Error'; 7 | import AppAPI from '@lib/api'; 8 | 9 | /* Component ==================================================================== */ 10 | class ConversationView extends Component { 11 | static componentName = 'ConversationView'; 12 | 13 | static propTypes = { 14 | conversation: PropTypes.object.isRequired, 15 | user: PropTypes.object.isRequired, 16 | sendMessage: PropTypes.func.isRequired, 17 | getMessages: PropTypes.func.isRequired 18 | }; 19 | 20 | constructor(props) { 21 | super(props); 22 | 23 | this.state = { 24 | typingText: null, 25 | isLoadingEarlier: false, 26 | isSendingTypingRequest: false, 27 | error: null 28 | }; 29 | 30 | this.isMounted = false; 31 | this.onSend = this.onSend.bind(this); 32 | this.onLoadEarlier = this.onLoadEarlier.bind(this); 33 | this.onInputTextChanged = this.onInputTextChanged.bind(this); 34 | 35 | this.isAlright = null; 36 | } 37 | 38 | componentWillMount() { 39 | this.isMounted = true; 40 | } 41 | 42 | componentDidMount = () => { 43 | this.fetchMessages(); 44 | } 45 | 46 | componentWillUnmount() { 47 | this.isMounted = false; 48 | } 49 | 50 | onLoadEarlier = async() => { 51 | this.setState({isLoadingEarlier: true}); 52 | 53 | await this.props.getMessages() 54 | .then(() => { 55 | this.setState({ 56 | isLoadingEarlier: false, 57 | error: null 58 | }); 59 | }).catch((err) => { 60 | const error = AppAPI.handleError(err); 61 | this.setState({ 62 | isLoadingEarlier: false, 63 | error 64 | }); 65 | }); 66 | }; 67 | 68 | onSend = (messages = []) => { 69 | const {conversation} = this.props; 70 | 71 | messages.map((message) => { 72 | const tmpMessage = { 73 | body: message.text, 74 | date: message.date ? message.date : null, 75 | dir: 1, 76 | id: message.id ? message.id : null, 77 | sent: false, 78 | unread: true 79 | }; 80 | 81 | this.props.sendMessage(Object.assign({}, tmpMessage), conversation.id); 82 | 83 | return null; 84 | 85 | // todo: need to catch error and give retry to user 86 | }); 87 | }; 88 | 89 | onInputTextChanged = () => { 90 | const {isSendingTypingRequest} = this.state; 91 | 92 | const {conversation} = this.props; 93 | 94 | if (!isSendingTypingRequest) { 95 | this.setState({isSendingTypingRequest: true}); 96 | } 97 | 98 | if (!isSendingTypingRequest) { 99 | AppAPI.conversations.post({id: conversation.id, action: 'typing'}) 100 | .finally(() => { 101 | this.setState({ 102 | isSendingTypingRequest: false 103 | }); 104 | }); 105 | } 106 | }; 107 | 108 | loadEarlier = () => { 109 | const {conversation} = this.props; 110 | 111 | if (conversation.messages.length > 1) { 112 | return conversation.messages.length < conversation.total; 113 | } 114 | return false; 115 | }; 116 | 117 | fetchMessages = async() => { 118 | const {getMessages, conversation} = this.props; 119 | 120 | // Forgot to pass conversation id? 121 | if (!conversation.id) { 122 | this.setState({ 123 | error: ErrorMessages.missingConversationID 124 | }); 125 | } 126 | 127 | await getMessages() 128 | .then(() => { 129 | this.setState({ 130 | error: null 131 | }); 132 | }) 133 | .catch((err) => { 134 | const error = AppAPI.handleError(err); 135 | this.setState({ 136 | error 137 | }); 138 | }); 139 | }; 140 | 141 | render() { 142 | const {error} = this.state; 143 | 144 | const {conversation} = this.props; 145 | 146 | if (conversation && error) {return ;} 147 | 148 | return ( 149 | 150 | ); 151 | } 152 | } 153 | 154 | /* Export Component ==================================================================== */ 155 | export default ConversationView; 156 | -------------------------------------------------------------------------------- /src/containers/main/home/stream/StreamContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Stream Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as StreamActions from '@redux/core/stream/actions'; 8 | 9 | // Selectors 10 | import {streamSelector} from '@redux/core/stream/selectors'; 11 | 12 | import PostsListingRender from './StreamView'; 13 | 14 | /* Redux ==================================================================== */ 15 | const mapStateToProps = state => ({ 16 | user: state.user, 17 | postsListing: streamSelector(state) 18 | }); 19 | 20 | // Any actions to map to the component? 21 | const mapDispatchToProps = { 22 | getPosts: StreamActions.getPosts, 23 | likePost: StreamActions.likePost, 24 | watchPost: StreamActions.watchPost, 25 | unwatchPost: StreamActions.unwatchPost, 26 | featurePost: StreamActions.featurePost, 27 | deletePost: StreamActions.deletePost, 28 | reportPost: StreamActions.reportPost, 29 | updateSectionIndex: StreamActions.updateSectionIndex 30 | }; 31 | 32 | export default connect(mapStateToProps, mapDispatchToProps)(PostsListingRender); 33 | -------------------------------------------------------------------------------- /src/containers/main/home/stream/comments/CommentsContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of Comments for a Post 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as StreamActions from '@redux/core/stream/actions'; 8 | 9 | // selector 10 | import {postSelector} from '@redux/core/stream/selectors'; 11 | 12 | import CommentsListingRender from './CommentsView'; 13 | 14 | /* Redux ==================================================================== */ 15 | 16 | const mapStateToProps = (state, ownProps) => ({ 17 | user: state.user, 18 | post: postSelector(state, ownProps) 19 | }); 20 | 21 | const mapDispatchToProps = { 22 | getComments: StreamActions.getComments, 23 | leaveComment: StreamActions.leaveComment 24 | }; 25 | 26 | const mergeProps = (stateProps, dispatchProps, ownProps) => ({ 27 | user: stateProps.user, 28 | post: stateProps.post, 29 | getComments: () => dispatchProps.getComments(ownProps.postID), 30 | leaveComment: dispatchProps.leaveComment 31 | }); 32 | 33 | export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(CommentsListingRender); 34 | -------------------------------------------------------------------------------- /src/containers/main/system-notifications/SystemNotificationsContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Notifications Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as SystemNotificationsActions from '@redux/core/system-notifications/actions'; 8 | 9 | // Selectors 10 | import {systemNotificationsSelector} from '@redux/core/system-notifications/selectors'; 11 | 12 | import SystemNotificationsListingRender from './SystemNotificationsView'; 13 | 14 | /* Redux ==================================================================== */ 15 | const mapStateToProps = state => ({ 16 | user: state.user, 17 | systemNotificationsListing: systemNotificationsSelector(state) 18 | }); 19 | 20 | // Any actions to map to the component? 21 | const mapDispatchToProps = { 22 | getSystemNotifications: SystemNotificationsActions.getSystemNotifications 23 | }; 24 | 25 | export default connect(mapStateToProps, mapDispatchToProps)(SystemNotificationsListingRender); 26 | -------------------------------------------------------------------------------- /src/containers/main/user-profile/Gifts/UserGiftsRender.js: -------------------------------------------------------------------------------- 1 | // Todo : Need to get Gifts of profile from another api 2 | import React, {Component} from 'react'; 3 | import { 4 | ListView, 5 | StyleSheet, 6 | View, 7 | Image, 8 | TouchableOpacity 9 | } from 'react-native'; 10 | 11 | import PropTypes from 'prop-types'; 12 | 13 | // Actions 14 | import {Actions} from 'react-native-router-flux'; 15 | 16 | // Consts and libs 17 | import {AppStyles, AppSizes, AppFonts} from '@theme/'; 18 | import Error from '@components/general/Error'; 19 | import {ErrorMessages} from '@constants/'; 20 | 21 | // Components 22 | import {Text} from '@ui/'; 23 | 24 | const styles = StyleSheet.create({ 25 | row: { 26 | flex: 1, 27 | flexDirection: 'row', 28 | justifyContent: 'space-between' 29 | } 30 | }); 31 | 32 | // Todo: Move component to another file 33 | /* Component ==================================================================== */ 34 | // eslint-disable-next-line no-unused-vars 35 | class GiftGrid extends Component { 36 | static propTypes = { 37 | data: PropTypes.array.isRequired, 38 | itemsPerRow: PropTypes.number.isRequired, 39 | itemMargin: PropTypes.number.isRequired, 40 | renderItem: PropTypes.func.isRequired 41 | }; 42 | 43 | constructor() { 44 | super(); 45 | 46 | this.state = { 47 | data: new ListView.DataSource({ 48 | // Todo: Fix me 49 | // eslint-disable-next-line no-unused-expressions 50 | rowHasChanged: (r1, r2) => { r1 !== r2; } 51 | }) 52 | }; 53 | } 54 | 55 | buildRows(items, itemsPerRow = 3) { 56 | return items.reduce((rows, item, idx) => { 57 | if (idx % itemsPerRow === 0 && idx > 0) {rows.push([]);} 58 | rows[rows.length - 1].push(item); 59 | return rows; 60 | }, [[]]); 61 | } 62 | 63 | renderRow(items) { 64 | const {itemsPerRow} = this.props; 65 | const margin = this.props.itemMargin || 1; 66 | 67 | const totalMargin = margin * (itemsPerRow - 1); 68 | const itemWidth = Math.floor((AppSizes.screen.width - totalMargin) / itemsPerRow); 69 | const adjustedMargin = (AppSizes.screen.width - (itemsPerRow * itemWidth)) / (itemsPerRow - 1); 70 | 71 | return ( 72 | 73 | { items.map(item => this.props.renderItem(item, itemWidth)) } 74 | { itemsPerRow - items.length > 0 && } 75 | 76 | ); 77 | } 78 | 79 | render() { 80 | const rows = this.buildRows(this.props.data, this.props.itemsPerRow); 81 | 82 | return ( 83 | 91 | ); 92 | } 93 | } 94 | 95 | class UserGiftsRender extends Component { 96 | static propTypes = { 97 | pid: PropTypes.string.isRequired 98 | }; 99 | 100 | constructor(props) { 101 | super(props); 102 | // this.state = { 103 | // items: this.props.gifts 104 | // }; 105 | } 106 | 107 | renderItem = (item, itemSize) => ( 108 | { Actions.userProfileView({userID: item.giverid}); }} style={[AppStyles.centerAligned, {width: itemSize, height: itemSize}]}> 109 | 119 | 128 | {item.giver} 135 | 136 | 137 | 138 | 139 | ) 140 | // 146 | 147 | render() { 148 | return ( 149 | 150 | ); 151 | } 152 | } 153 | 154 | export default UserGiftsRender; 155 | -------------------------------------------------------------------------------- /src/containers/main/user-profile/Info/UserInfoRender.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | View, 5 | ScrollView 6 | } from 'react-native'; 7 | 8 | import moment from 'moment'; 9 | 10 | // Consts and Libs 11 | import {AppStyles} from '@theme/'; 12 | 13 | // Components 14 | import {Icon, Text} from '@components/ui'; 15 | 16 | /* Styles ==================================================================== */ 17 | 18 | // const styles = StyleSheet.create({ 19 | // 20 | // }); 21 | 22 | // Todo: Move component to another file 23 | /* Component ==================================================================== */ 24 | class RenderRow extends Component { 25 | static componentName = 'RenderRow'; 26 | 27 | static propTypes = { 28 | icon: PropTypes.string.isRequired, 29 | iconType: PropTypes.string.isRequired, 30 | item: PropTypes.string.isRequired, 31 | value: PropTypes.string.isRequired 32 | }; 33 | 34 | render = () => ( 35 | 36 | 37 | 38 | 39 | 46 | 47 | {this.props.item} 48 | 49 | 50 | 51 | {this.props.item !== 'Additional Information' && 52 | 53 | 54 | {this.props.value} 55 | 56 | 57 | } 58 | 59 | 60 | {this.props.item === 'Additional Information' && 61 | 62 | 63 | {this.props.value} 64 | 65 | 66 | } 67 | 68 | 69 | 70 | ) 71 | } 72 | 73 | /* Component ==================================================================== */ 74 | class UserInfoRender extends Component { 75 | static componentName = 'UserInfoRender'; 76 | 77 | static propTypes = { 78 | profile: PropTypes.object.isRequired 79 | }; 80 | 81 | render = () => { 82 | const {profile} = this.props; 83 | 84 | return ( 85 | { this.scrollView = a; }} 87 | style={[AppStyles.container]}> 88 | 89 | 95 | 96 | 102 | 103 | 109 | 110 | 118 | 119 | 125 | 126 | 127 | ); 128 | } 129 | } 130 | 131 | /* Export Component ==================================================================== */ 132 | export default UserInfoRender; 133 | -------------------------------------------------------------------------------- /src/containers/main/user-profile/Photos/UserPhotosRender.js: -------------------------------------------------------------------------------- 1 | // Todo : Need to get Photo of profile from another api 2 | import React, {Component} from 'react'; 3 | import { 4 | // InteractionManager, 5 | ListView, 6 | StyleSheet, 7 | View 8 | } from 'react-native'; 9 | 10 | import PropTypes from 'prop-types'; 11 | 12 | import {AppStyles, AppSizes} from '@theme/'; 13 | 14 | import {Image, Text} from '@ui/'; 15 | import Loading from '@components/general/Loading'; 16 | import Error from '@components/general/Error'; 17 | import {ErrorMessages} from '@constants/'; 18 | 19 | import {getImageURL} from '@lib/util'; 20 | import AppAPI from '@lib/api'; 21 | 22 | const styles = StyleSheet.create({ 23 | row: { 24 | flex: 1, 25 | flexDirection: 'row', 26 | justifyContent: 'space-between' 27 | } 28 | }); 29 | 30 | // Todo: Move component to another file 31 | // eslint-disable-next-line no-unused-vars 32 | class PhotoGrid extends React.Component { 33 | static propTypes = { 34 | data: PropTypes.array.isRequired, 35 | itemsPerRow: PropTypes.number.isRequired, 36 | itemMargin: PropTypes.number.isRequired, 37 | renderItem: PropTypes.func.isRequired 38 | }; 39 | 40 | constructor() { 41 | super(); 42 | 43 | this.state = { 44 | data: new ListView.DataSource({ 45 | // Todo: Fix me 46 | // eslint-disable-next-line no-unused-expressions 47 | rowHasChanged: (r1, r2) => { r1 !== r2; } 48 | }) 49 | }; 50 | } 51 | 52 | buildRows(items, itemsPerRow = 3) { 53 | return items.reduce((rows, item, idx) => { 54 | if (idx % itemsPerRow === 0 && idx > 0) {rows.push([]);} 55 | rows[rows.length - 1].push(item); 56 | return rows; 57 | }, [[]]); 58 | } 59 | 60 | renderRow(items) { 61 | const {itemsPerRow} = this.props; 62 | const margin = this.props.itemMargin || 1; 63 | 64 | const totalMargin = margin * (itemsPerRow - 1); 65 | const itemWidth = Math.floor((AppSizes.screen.width - totalMargin) / itemsPerRow); 66 | const adjustedMargin = (AppSizes.screen.width - (itemsPerRow * itemWidth)) / (itemsPerRow - 1); 67 | 68 | return ( 69 | 70 | { items.map(item => this.props.renderItem(item, itemWidth)) } 71 | { itemsPerRow - items.length > 0 && } 72 | 73 | ); 74 | } 75 | 76 | render() { 77 | const rows = this.buildRows(this.props.data, this.props.itemsPerRow); 78 | 79 | return ( 80 | 86 | ); 87 | } 88 | } 89 | 90 | class UserPhotosRender extends Component { 91 | static propTypes = { 92 | pid: PropTypes.string.isRequired 93 | }; 94 | constructor(props) { 95 | super(props); 96 | this.state = { 97 | photos: null, 98 | error: null, 99 | isLoading: false 100 | }; 101 | } 102 | 103 | // componentDidMount() { 104 | // InteractionManager.runAfterInteractions(() => { 105 | // this.fetchUserPhotos(); 106 | // }); 107 | // } 108 | 109 | fetchUserPhotos = () => { 110 | this.setState({isLoading: true, error: null}); 111 | 112 | const {pid} = this.props; 113 | 114 | AppAPI.people.get({id: pid, mini: true, photos: true}) 115 | .then((res) => { 116 | this.setState({ 117 | photos: res.photos, 118 | isLoading: false, 119 | error: null 120 | }); 121 | }).catch((err) => { 122 | const error = AppAPI.handleError(err); 123 | this.setState({ 124 | error 125 | }); 126 | }); 127 | } 128 | 129 | renderItem = (item, itemSize) => ( 130 | 139 | 140 | ) 141 | 142 | render() { 143 | const {isLoading, photos, error} = this.state; 144 | 145 | if (isLoading && !photos) { 146 | return ; 147 | } 148 | 149 | // Todo: Better Error handling 150 | if (error) { 151 | return Cant Load Photos; 152 | } 153 | 154 | // 160 | 161 | 162 | return ( 163 | 164 | ); 165 | } 166 | } 167 | 168 | export default UserPhotosRender; 169 | -------------------------------------------------------------------------------- /src/containers/main/user-profile/Posts/UserPostsContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Stream Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | // Actions 7 | import * as StreamActions from '@redux/core/stream/actions'; 8 | 9 | // Selectors 10 | import {userPostsSelector} from '@redux/core/stream/selectors'; 11 | 12 | import PostsListingRender from './UserPostsView'; 13 | 14 | /* Redux ==================================================================== */ 15 | const mapStateToProps = (state, ownProps) => ({ 16 | user: state.user, 17 | postsListing: userPostsSelector(state, ownProps) 18 | }); 19 | 20 | // Any actions to map to the component? 21 | const mapDispatchToProps = { 22 | getUserPosts: StreamActions.getUserPosts, 23 | likePost: StreamActions.likePost, 24 | watchPost: StreamActions.watchPost, 25 | unwatchPost: StreamActions.unwatchPost, 26 | featurePost: StreamActions.featurePost, 27 | deletePost: StreamActions.deletePost, 28 | reportPost: StreamActions.reportPost 29 | }; 30 | 31 | export default connect(mapStateToProps, mapDispatchToProps)(PostsListingRender); 32 | -------------------------------------------------------------------------------- /src/containers/main/user-profile/UserProfileContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UserProfile Container 3 | */ 4 | import {connect} from 'react-redux'; 5 | 6 | import UserProfileRender from './UserProfileView'; 7 | 8 | /* Redux ==================================================================== */ 9 | const mapStateToProps = state => ({ 10 | user: state.user 11 | }); 12 | 13 | // Any actions to map to the component? 14 | const mapDispatchToProps = { 15 | }; 16 | 17 | export default connect(mapStateToProps, mapDispatchToProps)(UserProfileRender); 18 | -------------------------------------------------------------------------------- /src/containers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@containers" 3 | } 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Index - this is where everything 3 | * starts - but offloads to app.js 4 | */ 5 | /* global __DEV__ */ 6 | import React from 'react'; 7 | import {Platform, AsyncStorage} from 'react-native'; // we need to import AsyncStorage to use as a storage enging 8 | import {persistStore, autoRehydrate} from 'redux-persist'; // for persist redux 9 | import {applyMiddleware, compose, createStore} from 'redux'; 10 | import {connect, Provider} from 'react-redux'; 11 | import {Router} from 'react-native-router-flux'; 12 | 13 | import logger from 'redux-logger'; 14 | import thunk from 'redux-thunk'; 15 | 16 | // Consts and Libs 17 | import {AppStyles} from '@theme/'; 18 | import AppRoutes from '@navigation/'; 19 | import {AnalyticsMiddleware, socketMiddleware, apiMiddleware} from '@redux/middleware/'; 20 | 21 | // All redux reducers (rolled into one mega-reducer) 22 | import rootReducer from '@redux/index'; 23 | 24 | // Connect RNRF with Redux 25 | const RouterWithRedux = connect()(Router); 26 | 27 | // Load middleware 28 | let middleware = [ 29 | AnalyticsMiddleware, 30 | socketMiddleware, 31 | apiMiddleware, 32 | thunk // Allows action creators to return functions (not just plain objects) 33 | ]; 34 | 35 | if (__DEV__) { 36 | // Dev-only middleware 37 | middleware = [ 38 | ...middleware, 39 | logger // Logs state changes to the dev console 40 | ]; 41 | } 42 | 43 | // Init redux store (using the given reducer & middleware) 44 | const store = compose( 45 | applyMiddleware(...middleware), 46 | autoRehydrate(), 47 | )(createStore)(rootReducer); 48 | 49 | // store dispatch in global for external access 50 | global.dispatch = store.dispatch; 51 | 52 | // begin periodically persisting the store 53 | persistStore(store, {whitelist: ['user'], storage: AsyncStorage}); 54 | 55 | /* Component ==================================================================== */ 56 | // Wrap App in Redux provider (makes Redux available to all sub-components) 57 | export default function AppContainer() { 58 | return ( 59 | 60 | 66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/lib/helper.js: -------------------------------------------------------------------------------- 1 | /* eslint no-undef: "off" */ 2 | 3 | import RNFetchBlob from 'react-native-fetch-blob'; 4 | 5 | import {APIConfig} from '@constants/'; 6 | 7 | export default function imageUploader(imageUri) { 8 | const apiToken = AsyncStorage.getItem('api/token').then(res => res); 9 | 10 | RNFetchBlob.fs.exists(imageUri) 11 | .then((exist) => { 12 | if (!exist || !apiToken) { 13 | reject(); 14 | } 15 | }) 16 | .catch(() => reject()); 17 | 18 | return RNFetchBlob.fetch('POST', `${APIConfig.apiUrl}/upload-image.ashx`, { 19 | 'Content-Type': 'jpg/jpeg', 20 | 'X-AUTH-TOKEN': apiToken 21 | }, RNFetchBlob.wrap(imageUri)); 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lib" 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/util.js: -------------------------------------------------------------------------------- 1 | import {AppConfig} from '@constants/'; 2 | 3 | const getImageURL = (imageID, lowSize) => { 4 | if (!imageID) {return `${AppConfig.urls.imageCDN}1/120`;} 5 | 6 | if (lowSize) { 7 | return `${AppConfig.urls.imageCDN + imageID}/120`; 8 | } 9 | return `${AppConfig.urls.imageCDN + imageID}/500`; 10 | }; 11 | 12 | const getPitagorasZ = (x, y) => ( 13 | Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) 14 | ); 15 | 16 | const backgroundValueCalculation = (x, y, BACKGROUND_VALUES) => ( 17 | 4 / 3 * BACKGROUND_VALUES.MAX - getPitagorasZ(x, y) 18 | ); 19 | 20 | export { 21 | getImageURL, 22 | backgroundValueCalculation 23 | }; 24 | -------------------------------------------------------------------------------- /src/navigation/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Auth Scenes 3 | */ 4 | import React from 'react'; 5 | import {Scene, ActionConst} from 'react-native-router-flux'; 6 | 7 | // Consts and Libs 8 | import {AppConfig} from '@constants/'; 9 | 10 | // Scenes 11 | import Authenticate from '@containers/auth/AuthenticateContainer'; 12 | import AuthWebView from '@containers/auth/WebView'; 13 | 14 | /* Routes ==================================================================== */ 15 | const scenes = ( 16 | 17 | 24 | 33 | 34 | ); 35 | 36 | export default scenes; 37 | -------------------------------------------------------------------------------- /src/navigation/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Navigation 3 | */ 4 | import React from 'react'; 5 | import {Actions, Scene, ActionConst} from 'react-native-router-flux'; 6 | 7 | // Consts and Libs 8 | import {AppConfig} from '@constants/'; 9 | 10 | // Containers 11 | import AppLaunch from '@containers/launch/LaunchContainer'; 12 | 13 | // Scenes 14 | import AuthScenes from './auth'; 15 | import TabsScenes from './tabs'; 16 | import SubScenes from './sub-scenes'; 17 | 18 | /* Routes ==================================================================== */ 19 | const route = () => ( 20 | 21 | 27 | 28 | {/* Auth */} 29 | {AuthScenes} 30 | 31 | {/* Main App */} 32 | 33 | {/* Tabbar */} 34 | {TabsScenes} 35 | 36 | {/* Sub-Scenes */} 37 | {SubScenes} 38 | 39 | 40 | 41 | ); 42 | 43 | export default Actions.create(route()); 44 | -------------------------------------------------------------------------------- /src/navigation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@navigation" 3 | } 4 | -------------------------------------------------------------------------------- /src/navigation/sub-scenes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tabs Scenes 3 | */ 4 | import React from 'react'; 5 | import {Scene} from 'react-native-router-flux'; 6 | 7 | // Consts and Libs 8 | import {AppConfig} from '@constants/'; 9 | 10 | // Components 11 | import {BackButton} from '@components/ui'; 12 | 13 | // Containers 14 | import UserProfileContainer from '@containers/main/user-profile/UserProfileContainer'; 15 | import CommentsContainer from '@containers/main/home/stream/comments/CommentsContainer'; 16 | import ConversationContainer from '@containers/main/home/conversations/conversation/ConversationContainer'; 17 | 18 | /* Routes ==================================================================== */ 19 | const scenes = ([ 20 | 21 | `UserProfile: View ${props.userID}`} 27 | />, 28 | 29 | ()} 32 | key='commentsView' 33 | component={CommentsContainer} 34 | title='COMMENTS' 35 | analyticsDesc={props => `CommentsView: View ${props.postID}`} 36 | />, 37 | 38 | ()} 42 | key='conversationView' 43 | component={ConversationContainer} 44 | analyticsDesc='ConversationView: Chat View Someone' 45 | /> 46 | 47 | ]); 48 | 49 | export default scenes; 50 | -------------------------------------------------------------------------------- /src/navigation/tabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tabs Scenes 3 | */ 4 | import React from 'react'; 5 | import {Scene} from 'react-native-router-flux'; 6 | 7 | // Consts and Libs 8 | import {AppStyles, AppSizes, AppColors} from '@theme/'; 9 | 10 | // Components 11 | import {TabIcon} from '@ui/'; 12 | 13 | // Scenes 14 | import Placeholder from '@components/general/Placeholder'; 15 | 16 | // Containers 17 | import Home from '@containers/main/home/HomeContainer'; 18 | import SystemNotificationsContainer from '@containers/main/system-notifications/SystemNotificationsContainer'; 19 | 20 | const sceneStyleProps = { 21 | sceneStyle: { 22 | backgroundColor: AppColors.background, 23 | paddingBottom: AppSizes.tabbarHeight 24 | } 25 | }; 26 | 27 | /* Routes ==================================================================== */ 28 | const scenes = ( 29 | 30 | 31 | } 36 | component={Home} 37 | analyticsDesc='Home: Main' 38 | /> 39 | 40 | } 47 | analyticsDesc='People: People' 48 | /> 49 | 50 | } 57 | analyticsDesc='Posts: New' 58 | /> 59 | 60 | } 66 | analyticsDesc='SystemNotifications: Notifications-List' 67 | /> 68 | 69 | } 76 | analyticsDesc='Profile: Profile' 77 | /> 78 | 79 | 80 | ); 81 | 82 | export default scenes; 83 | -------------------------------------------------------------------------------- /src/redux/core/conversations/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Conversations Actions 3 | */ 4 | 5 | import AppAPI from '@lib/api'; 6 | 7 | export function getConversations(startFrom) { 8 | return dispatch => AppAPI.conversations.get({start: startFrom, count: 25, include: 'text'}) 9 | .then((res) => { 10 | dispatch({ 11 | type: 'CONVERSATIONS_REPLACE', 12 | data: res 13 | }); 14 | }); 15 | } 16 | 17 | export function deleteConversation(pid) { 18 | return (dispatch) => { 19 | // for better ux we delete conversation first and then send delete request to server 20 | dispatch({ 21 | type: 'CONVERSATION_DELETE', 22 | id: pid 23 | }); 24 | 25 | return AppAPI.conversations.post({id: pid, action: 'delete'}); 26 | }; 27 | } 28 | 29 | export function deleteConversations() { 30 | return dispatch => AppAPI.conversations.post({action: 'delete'}) 31 | .finally(() => { 32 | dispatch({ 33 | type: 'CONVERSATIONS_DELETE' 34 | }); 35 | }); 36 | } 37 | 38 | export function uninitializeConversation(pid) { 39 | return (dispatch) => { 40 | dispatch({ 41 | type: 'CONVERSATION_UNINITIALIZE', 42 | id: pid 43 | }); 44 | }; 45 | } 46 | 47 | export function getMessages(params) { 48 | return (dispatch) => { 49 | let startFrom = 0; 50 | 51 | if (params.initialized) { 52 | startFrom = params.last; 53 | } 54 | 55 | return AppAPI.conversations.get({id: params.id, count: 15, start: startFrom}) 56 | .then((res) => { 57 | // we don't need to pass id from server , we create default uuid 58 | // we delete unread too cuz its not working fine 59 | res.map((obj) => { delete obj.id; delete obj.unread; return null; }); 60 | dispatch({ 61 | type: 'MESSAGES_REPLACE', 62 | data: res, 63 | id: params.id 64 | }); 65 | }) // then we update messages receipt status in async 66 | .then(() => { 67 | AppAPI.conversations.get({id: params.id, action: 'receipt'}) 68 | .then((res) => { 69 | if (res === 1) { 70 | dispatch({ 71 | type: 'MESSAGES_READ', 72 | id: params.id 73 | }); 74 | } 75 | }); 76 | }); 77 | }; 78 | } 79 | 80 | export function sendMessage(message, pid) { 81 | return (dispatch) => { 82 | // for better user experience first we show message as sending and then sent http request 83 | dispatch({ 84 | type: 'MESSAGE_SEND', 85 | data: message, 86 | id: pid 87 | }); 88 | // sending http request and then set message as sent 89 | return AppAPI.conversations.post({id: pid}, message.body) 90 | .then((res) => { 91 | dispatch({ 92 | type: 'MESSAGE_SENT', 93 | id: message.id, 94 | msgid: res.id 95 | 96 | }); 97 | }) // then update message receipt status but better send with 4 sec delay 98 | .then(() => new Promise(r => setTimeout(r, 4000))) 99 | .then(() => { 100 | AppAPI.conversations.get({id: pid, action: 'receipt'}) 101 | .then((res) => { 102 | if (res === 1) { 103 | dispatch({ 104 | type: 'MESSAGES_READ', 105 | id: pid 106 | }); 107 | } 108 | }); 109 | }); 110 | }; 111 | } 112 | -------------------------------------------------------------------------------- /src/redux/core/conversations/models.js: -------------------------------------------------------------------------------- 1 | import {ORM} from 'redux-orm'; 2 | import {attr, many, Model} from 'redux-orm'; 3 | import uuid from 'uuid'; 4 | 5 | /* Models ==================================================================== */ 6 | 7 | export class Message extends Model { 8 | toString() { 9 | return `Message : ${this.body}`; 10 | } 11 | 12 | static get fields() { 13 | return { 14 | body: attr(), 15 | date: attr(), 16 | dir: attr(), 17 | id: attr({getDefault: uuid.v4}), 18 | msgid: attr(), 19 | sent: attr({getDefault: () => true}), 20 | unread: attr({getDefault: () => true}) 21 | }; 22 | } 23 | 24 | static get modelName() { 25 | return 'Message'; 26 | } 27 | 28 | static get options() { 29 | return { 30 | idAttribute: 'id' 31 | }; 32 | } 33 | } 34 | 35 | export class Conversation extends Model { 36 | toString() { 37 | return `Conversation: ${this.name}`; 38 | } 39 | 40 | static get fields() { 41 | return { 42 | id: attr(), 43 | messages: many('Message'), 44 | gold: attr(), 45 | img: attr(), 46 | last: attr(), 47 | lastid: attr(), 48 | msg: attr(), 49 | name: attr(), 50 | new: attr(), 51 | staff: attr(), 52 | total: attr(), 53 | initialized: attr({getDefault: false}) 54 | }; 55 | } 56 | 57 | static get modelName() { 58 | return 'Conversation'; 59 | } 60 | 61 | static get options() { 62 | return { 63 | idAttribute: 'id' 64 | }; 65 | } 66 | } 67 | 68 | /* Schema ==================================================================== */ 69 | 70 | const schema = new ORM(); 71 | schema.register(Message, Conversation); 72 | 73 | /* Export Schema ==================================================================== */ 74 | 75 | export default schema; 76 | -------------------------------------------------------------------------------- /src/redux/core/conversations/reducer.js: -------------------------------------------------------------------------------- 1 | import schema from './models'; 2 | 3 | /** 4 | * Conversations Reducer 5 | */ 6 | 7 | // Set initial state 8 | 9 | const initialState = {...schema.getEmptyState(), UnreadCount: 0}; 10 | 11 | export default function conversationsReducer(state = initialState, action) { 12 | switch (action.type) { 13 | case 'CONVERSATIONS_REPLACE': { 14 | const session = schema.session(state); 15 | const {Conversation} = session; 16 | 17 | if (action.data && typeof action.data === 'object') { 18 | action.data.map((row) => { 19 | // check if conversation exist update 20 | if (Conversation.hasId(row.id)) { 21 | Conversation.withId(row.id).update(row); 22 | } else { // if not create 23 | Conversation.create(row); 24 | } 25 | 26 | return null; 27 | }); 28 | } 29 | 30 | return session.state; 31 | } 32 | 33 | case 'CONVERSATIONS_UNREAD_COUNT_UPDATE': { 34 | if (typeof action.data === 'number') { 35 | return { 36 | ...state, 37 | UnreadCount: action.data 38 | }; 39 | } 40 | 41 | return state; 42 | } 43 | 44 | case 'CONVERSATIONS_DELETE': { 45 | const session = schema.session(state); 46 | const {Conversation} = session; 47 | 48 | Conversation.all().delete(); 49 | 50 | return session.state; 51 | } 52 | 53 | case 'CONVERSATION_DELETE': { 54 | const session = schema.session(state); 55 | const {Conversation} = session; 56 | 57 | if (action.id && typeof action.id === 'string') { 58 | if (Conversation.hasId(action.id)) { 59 | Conversation.withId(action.id).delete(); 60 | } 61 | } 62 | 63 | return session.state; 64 | } 65 | 66 | case 'CONVERSATION_UNINITIALIZE': { 67 | const session = schema.session(state); 68 | const {Conversation} = session; 69 | 70 | if (action.id && typeof action.id === 'string') { 71 | if (Conversation.hasId(action.id)) { 72 | Conversation.withId(action.id).update({initialized: false, new: 0}); 73 | } 74 | } 75 | 76 | return session.state; 77 | } 78 | 79 | case 'MESSAGES_REPLACE': { 80 | const session = schema.session(state); 81 | const {Conversation, Message} = session; 82 | 83 | if (action.data && typeof action.data === 'object') { 84 | const conversation = Conversation.withId(action.id); 85 | conversation.update({initialized: true}); 86 | 87 | action.data.map((row) => { 88 | // check if message exist or not , we dont need duplicate messages , right ? 89 | if (!Message.filter({msgid: row.msgid}).exists()) { 90 | conversation.messages.add(Message.create(row)); 91 | } 92 | return null; 93 | }); 94 | } 95 | 96 | return session.state; 97 | } 98 | 99 | case 'MESSAGES_READ': { 100 | const session = schema.session(state); 101 | const {Conversation} = session; 102 | 103 | if (action.id && typeof action.id === 'string') { 104 | const conversation = Conversation.withId(action.id); 105 | 106 | conversation.messages.filter({unread: true}).toModelArray().map((message) => { 107 | message.update({unread: false}); 108 | return null; 109 | }); 110 | } 111 | 112 | return session.state; 113 | } 114 | 115 | case 'MESSAGE_SEND': { 116 | const session = schema.session(state); 117 | const {Conversation, Message} = session; 118 | 119 | // todo:should create conversation if not exist for new start conversation 120 | 121 | if (action.id && typeof action.id === 'string' && action.data && typeof action.data === 'object') { 122 | if (Conversation.hasId(action.id)) { 123 | const conversation = Conversation.withId(action.id); 124 | // create an message and link to conversation 125 | conversation.messages.add(Message.create(action.data)); 126 | // we need to update msg and date for conversation so 127 | // we don't need to update it from server on conversations list 128 | conversation.update({ 129 | msg: action.data.body, 130 | last: new Date().toISOString(), 131 | total: conversation.total + 1 132 | }); 133 | } 134 | } 135 | 136 | return session.state; 137 | } 138 | 139 | case 'MESSAGE_SENT': { 140 | const session = schema.session(state); 141 | const {Message} = session; 142 | 143 | // update message to sent and change messageId 144 | 145 | if (action.id && action.msgid) { 146 | if (Message.hasId(action.id)) { 147 | Message.withId(action.id).update({sent: true, msgid: action.msgid}); 148 | } 149 | } 150 | 151 | return session.state; 152 | } 153 | 154 | default: 155 | return state; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/redux/core/conversations/selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {createSelector as ormCreateSelector} from 'redux-orm'; 3 | import schema from './models'; 4 | 5 | export const ormSelector = state => state.conversations; 6 | 7 | export const conversationsSelector = createSelector( 8 | ormSelector, 9 | ormCreateSelector(schema, session => session.Conversation.all().orderBy(['last'], 'desc').toRefArray()), 10 | ); 11 | 12 | export const conversationSelector = createSelector( 13 | ormSelector, 14 | (state, props) => props, 15 | ormCreateSelector(schema, (session, props) => { 16 | const conversation = session.Conversation.withId(props.pid); 17 | 18 | const obj = Object.assign({}, conversation.ref); 19 | 20 | return Object.assign({}, obj, { 21 | messages: conversation.messages.orderBy(['date']).toRefArray(), 22 | lessParams: { 23 | last: conversation.messages.count() > 1 ? conversation.messages.orderBy(['date']).first().msgid : null, 24 | initialized: conversation.initialized, 25 | id: conversation.id 26 | 27 | } 28 | }); 29 | }), 30 | ); 31 | -------------------------------------------------------------------------------- /src/redux/core/router/reducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Router Reducer 3 | */ 4 | import {ActionConst} from 'react-native-router-flux'; 5 | 6 | // Set initial state 7 | const initialState = { 8 | scene: {} 9 | }; 10 | 11 | export default function routerReducer(state = initialState, action) { 12 | switch (action.type) { 13 | // focus action is dispatched when a new screen comes into focus 14 | case ActionConst.FOCUS: 15 | return { 16 | ...state, 17 | scene: action.scene 18 | }; 19 | 20 | // ...other actions 21 | 22 | default: 23 | return state; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/redux/core/stream/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Stream Actions 3 | */ 4 | 5 | import AppAPI from '@lib/api'; 6 | 7 | export function updateSectionIndex(sectionIndex) { 8 | return (dispatch) => { 9 | dispatch({ 10 | type: 'SECTION_INDEX_UPDATE', 11 | section: sectionIndex 12 | }); 13 | }; 14 | } 15 | 16 | /* Posts ==================================================================== */ 17 | 18 | export function getPosts(sectionIndex, startFrom) { 19 | return (dispatch) => { 20 | let section = 'Recent'; 21 | 22 | switch (sectionIndex) { 23 | case 0: 24 | section = 'Recent'; 25 | break; 26 | case 1: 27 | section = 'Following'; 28 | break; 29 | case 2: 30 | section = 'Hot'; 31 | break; 32 | default: 33 | break; 34 | } 35 | 36 | return AppAPI.stream.get({section, last: startFrom}) 37 | .then((res) => { 38 | dispatch({ 39 | type: 'POSTS_UPDATE', 40 | data: res, 41 | section: sectionIndex 42 | }); 43 | }); 44 | }; 45 | } 46 | 47 | export function getUserPosts(userID, startFrom) { 48 | return dispatch => AppAPI.people_info.get({pid: userID, section: 'posts', last: startFrom}) 49 | .then((res) => { 50 | dispatch({ 51 | type: 'POSTS_UPDATE', 52 | data: res, 53 | section: 3 54 | }); 55 | }); 56 | } 57 | 58 | export function likePost(postID, ownerId) { 59 | return (dispatch) => { 60 | dispatch({ 61 | type: 'POST_LIKE', 62 | id: postID 63 | }); 64 | return AppAPI.livestream.post({pid: postID, section: 'props', owner: ownerId}); 65 | }; 66 | } 67 | 68 | export function featurePost(postID) { 69 | return dispatch => AppAPI.livestream.post({pid: postID, section: 'feature'}) 70 | .then(() => { 71 | dispatch({ 72 | type: 'POST_FEATURE', 73 | id: postID 74 | }); 75 | }); 76 | } 77 | 78 | export function watchPost(postID) { 79 | return (dispatch) => { 80 | dispatch({ 81 | type: 'POST_WATCH', 82 | id: postID 83 | }); 84 | return AppAPI.livestream.get({pid: postID, section: 'watch'}); 85 | }; 86 | } 87 | 88 | export function unwatchPost(postID) { 89 | return (dispatch) => { 90 | dispatch({ 91 | type: 'POST_UNWATCH', 92 | id: postID 93 | }); 94 | return AppAPI.livestream.get({pid: postID, section: 'unwatch'}); 95 | }; 96 | } 97 | 98 | export function deletePost(postID) { 99 | return (dispatch) => { 100 | dispatch({ 101 | type: 'POST_DELETE', 102 | id: postID 103 | }); 104 | return AppAPI.livestream.post({pid: postID, section: 'delete'}); 105 | }; 106 | } 107 | 108 | export function reportPost(postID) { 109 | return (dispatch) => { 110 | dispatch({ 111 | type: 'POST_REPORT', 112 | id: postID 113 | }); 114 | return AppAPI.livestream.get({pid: postID, section: 'report'}); 115 | }; 116 | } 117 | 118 | /* Comments ==================================================================== */ 119 | 120 | export function getComments(postID) { 121 | return dispatch => AppAPI.livestream.get({pid: postID, section: 'comments'}) 122 | .then((res) => { 123 | dispatch({ 124 | type: 'COMMENTS_UPDATE', 125 | data: res, 126 | id: postID 127 | }); 128 | }); 129 | } 130 | 131 | export function leaveComment(postID, comment) { 132 | return (dispatch) => { 133 | // for better user experience first we show comment as sending and then sent http request 134 | dispatch({ 135 | type: 'COMMENTS_ADD', 136 | data: comment, 137 | id: postID 138 | }); 139 | // sending http request and then set message as sent 140 | return AppAPI.livestream.post({pid: postID, section: 'comments'}, comment.txt); 141 | }; 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src/redux/core/stream/models.js: -------------------------------------------------------------------------------- 1 | import {ORM, attr, many, Model} from 'redux-orm'; 2 | import uuid from 'uuid'; 3 | 4 | /* Models ==================================================================== */ 5 | 6 | export class Post extends Model { 7 | toString() { 8 | return `Post : ${this.txt}`; 9 | } 10 | 11 | static get fields() { 12 | return { 13 | id: attr({getDefault: uuid.v4}), 14 | comments: many('Comment'), 15 | section: attr({getDefault: () => []}), 16 | cc: attr(), 17 | d: attr(), 18 | date: attr(), 19 | featured: attr(), 20 | gp: attr(), 21 | gr: attr(), 22 | grId: attr(), 23 | img: attr(), 24 | lc: attr(), 25 | lcid: attr(), 26 | loc: attr(), 27 | name: attr(), 28 | new: attr(), 29 | pImg: attr(), 30 | pc: attr(), 31 | pid: attr(), 32 | t: attr(), 33 | txt: attr(), 34 | ul: attr(), 35 | ur: attr(), 36 | w: attr() 37 | }; 38 | } 39 | 40 | static get modelName() { 41 | return 'Post'; 42 | } 43 | 44 | static get options() { 45 | return { 46 | idAttribute: 'id' 47 | }; 48 | } 49 | } 50 | 51 | export class Comment extends Model { 52 | toString() { 53 | return `Comment : ${this.txt}`; 54 | } 55 | 56 | static get fields() { 57 | return { 58 | id: attr({getDefault: uuid.v4}), 59 | cc: attr(), 60 | d: attr(), 61 | date: attr({getDefault: () => { new Date().toISOString(); }}), 62 | featured: attr(), 63 | gp: attr(), 64 | img: attr(), 65 | lc: attr(), 66 | lcid: attr(), 67 | loc: attr(), 68 | name: attr(), 69 | new: attr(), 70 | pImg: attr(), 71 | pc: attr(), 72 | pid: attr(), 73 | t: attr(), 74 | txt: attr(), 75 | ul: attr(), 76 | ur: attr(), 77 | w: attr() 78 | }; 79 | } 80 | 81 | static get modelName() { 82 | return 'Comment'; 83 | } 84 | 85 | static get options() { 86 | return { 87 | idAttribute: 'id' 88 | }; 89 | } 90 | } 91 | 92 | /* Schema ==================================================================== */ 93 | 94 | const schema = new ORM(); 95 | schema.register(Post, Comment); 96 | 97 | /* Export Schema ==================================================================== */ 98 | 99 | export default schema; 100 | -------------------------------------------------------------------------------- /src/redux/core/stream/selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {createSelector as ormCreateSelector} from 'redux-orm'; 3 | import schema from './models'; 4 | 5 | export const ormSelector = state => state.stream; 6 | 7 | export const streamSelector = createSelector( 8 | ormSelector, 9 | state => state.stream.selectedSectionIndex, 10 | ormCreateSelector(schema, (session, selectedIndex) => { 11 | let Index = selectedIndex ; 12 | 13 | if (!Index) { 14 | Index = 0; 15 | } 16 | 17 | if (Index !== 2) { 18 | return session.Post.all().filter(post => post.section.includes(Index)).orderBy(['date'], 'desc').toRefArray(); 19 | } 20 | return session.Post.all().filter(post => post.section.includes(Index)).toRefArray(); 21 | }), 22 | ); 23 | 24 | export const postSelector = createSelector( 25 | ormSelector, 26 | (state, props) => props, 27 | ormCreateSelector(schema, (session, props) => { 28 | const post = session.Post.withId(props.postID); 29 | 30 | const obj = Object.assign({}, post.ref); 31 | 32 | return Object.assign({}, obj, { 33 | comments: post.comments.orderBy(['date'], 'desc').toRefArray() 34 | }); 35 | }), 36 | ); 37 | 38 | export const userPostsSelector = createSelector( 39 | ormSelector, 40 | (state, props) => props, 41 | ormCreateSelector(schema, (session, props) => session.Post.filter(post => post.pid === props.pid).orderBy(['date'], 'desc').toRefArray()), 42 | ); 43 | -------------------------------------------------------------------------------- /src/redux/core/system-notifications/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Notifications Actions 3 | */ 4 | 5 | import AppAPI from '@lib/api'; 6 | 7 | export function getSystemNotifications(startFrom) { 8 | return (dispatch) => { 9 | let latest = false; 10 | 11 | if (startFrom === -1) { 12 | latest = true; 13 | } 14 | 15 | return AppAPI.notifications.get({last: startFrom, latest}) 16 | .then((res) => { 17 | dispatch({ 18 | type: 'SYSTEM_NOTIFICATIONS_UPDATE', 19 | data: res 20 | }); 21 | }); 22 | }; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/redux/core/system-notifications/models.js: -------------------------------------------------------------------------------- 1 | import {ORM, attr, Model} from 'redux-orm'; 2 | 3 | /* Models ==================================================================== */ 4 | 5 | export class Notification extends Model { 6 | toString() { 7 | return `Notification : ${this.txt}`; 8 | } 9 | 10 | static get fields() { 11 | return { 12 | id: attr(), 13 | type: attr(), 14 | new: attr(), 15 | param: attr(), 16 | date: attr(), 17 | txt: attr(), 18 | img: attr() 19 | }; 20 | } 21 | 22 | static get modelName() { 23 | return 'Notification'; 24 | } 25 | 26 | static get options() { 27 | return { 28 | idAttribute: 'id' 29 | }; 30 | } 31 | } 32 | 33 | /* Schema ==================================================================== */ 34 | 35 | const schema = new ORM(); 36 | schema.register(Notification); 37 | 38 | /* Export Schema ==================================================================== */ 39 | 40 | export default schema; 41 | -------------------------------------------------------------------------------- /src/redux/core/system-notifications/reducer.js: -------------------------------------------------------------------------------- 1 | import schema from './models'; 2 | 3 | /** 4 | * System Notifications Reducer 5 | */ 6 | 7 | // Set initial state 8 | const initialState = {...schema.getEmptyState(), UnreadCount: 0}; 9 | 10 | export default function notificationsReducer(state = initialState, action) { 11 | switch (action.type) { 12 | case 'SYSTEM_NOTIFICATIONS_UPDATE': { 13 | const session = schema.session(state); 14 | const {Notification} = session; 15 | 16 | if (action.data && typeof action.data === 'object') { 17 | action.data.map((row) => { 18 | // check if notification not exist then create 19 | if (Notification.hasId(row.id)) { 20 | Notification.withId(row.id).update(row); 21 | } else { 22 | Notification.create(row); 23 | } 24 | 25 | return null; 26 | }); 27 | } 28 | 29 | return session.state; 30 | } 31 | case 'SYSTEM_NOTIFICATIONS_UNREAD_COUNT_UPDATE': { 32 | if (typeof action.data === 'number') { 33 | return { 34 | ...state, 35 | UnreadCount: action.data 36 | }; 37 | } 38 | 39 | return state; 40 | } 41 | 42 | default: 43 | return state; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/redux/core/system-notifications/selectors.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {createSelector as ormCreateSelector} from 'redux-orm'; 3 | import schema from './models'; 4 | 5 | export const ormSelector = state => state.systemNotifications; 6 | 7 | export const systemNotificationsSelector = createSelector( 8 | ormSelector, 9 | ormCreateSelector(schema, session => session.Notification.all().orderBy(['date'], 'desc').toRefArray()), 10 | ); 11 | -------------------------------------------------------------------------------- /src/redux/core/user/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User Actions 3 | */ 4 | 5 | import {AsyncStorage} from 'react-native'; 6 | 7 | import AppAPI from '@lib/api'; 8 | import {APIConfig} from '@constants/'; 9 | 10 | // check if there is any token or user logged in 11 | export function getLoginStatus() { 12 | // Todo: Fix me 13 | // eslint-disable-next-line no-unused-vars 14 | return dispatch => new Promise(async(resolve, reject) => { 15 | let apiToken; 16 | 17 | if (AppAPI.getToken) {apiToken = await AppAPI.getToken();} 18 | if (apiToken) { 19 | return resolve(apiToken); 20 | } 21 | return reject(); 22 | }); 23 | } 24 | 25 | /** 26 | * Login to Nearby with email and password and receive Token 27 | */ 28 | export function emailLogin(credentials, freshLogin) { 29 | // eslint-disable-next-line 30 | return dispatch => new Promise(async(resolve, reject) => { 31 | const userCredentials = credentials || null; 32 | 33 | // Force logout, before logging in 34 | if (freshLogin && AppAPI.deleteToken) {await AppAPI.deleteToken();} 35 | 36 | if (userCredentials) { 37 | AppAPI[APIConfig.tokenKey].get({ 38 | email: userCredentials.email, 39 | password: userCredentials.password, 40 | lat: 0, 41 | long: 0 42 | }).then(async(res) => { 43 | if (res.length < 1 || !res.length) { 44 | return reject(new Error('Invalid Email and/or Password!')); 45 | } 46 | 47 | // Save new Credentials to AsyncStorage 48 | await AsyncStorage.setItem('api/credentials', userCredentials.email); 49 | 50 | // Set token in AsyncStorage + memory 51 | await AsyncStorage.setItem('api/token', res); 52 | 53 | // Get user details from API, using user token 54 | return AppAPI.connect.get() 55 | .then(async(userData) => { 56 | dispatch({ 57 | type: 'USER_REPLACE', 58 | data: userData 59 | }); 60 | return resolve(userData); 61 | }).catch(err => reject(err)); 62 | }).catch(err => reject(err)); 63 | } else { 64 | return reject(); 65 | } 66 | 67 | 68 | }); 69 | } 70 | 71 | /** 72 | * Login to Nearby with facebook accessToken 73 | */ 74 | export function facebookLogin(accessToken, freshLogin) { 75 | return dispatch => new Promise(async(resolve, reject) => { 76 | // Force logout, before logging in 77 | if (freshLogin && AppAPI.deleteToken) {await AppAPI.deleteToken();} 78 | 79 | if (accessToken) { 80 | AppAPI.oauth.post({ 81 | token: accessToken, 82 | enhanced: true, 83 | provider: 2, 84 | lat: 0, 85 | long: 0 86 | }).then(async(res) => { 87 | if (!res.IsSuccess) { 88 | return reject(new Error('Cant login with facebook right now!')); 89 | } 90 | 91 | // Set token in AsyncStorage + memory 92 | await AsyncStorage.setItem('api/token', res.Token); 93 | 94 | // Get user details from API, using user token 95 | return AppAPI.connect.get() 96 | .then(async(userData) => { 97 | dispatch({ 98 | type: 'USER_REPLACE', 99 | data: userData 100 | }); 101 | return resolve(userData); 102 | }).catch(err => reject(err)); 103 | }).catch(err => reject(err)); 104 | } else { 105 | return reject(); 106 | } 107 | 108 | return reject(); 109 | }); 110 | } 111 | 112 | /** 113 | * Logout 114 | */ 115 | export function logout() { 116 | return dispatch => AppAPI.deleteToken() 117 | .then(() => { 118 | dispatch({ 119 | type: 'USER_REPLACE', 120 | data: {} 121 | }); 122 | }); 123 | } 124 | -------------------------------------------------------------------------------- /src/redux/core/user/reducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User Reducer 3 | */ 4 | 5 | // Set initial state 6 | const initialState = {}; 7 | 8 | export default function userReducer(state = initialState, action) { 9 | switch (action.type) { 10 | case 'USER_REPLACE': 11 | return action.data; 12 | 13 | default: 14 | return state; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/redux/core/ws/actions.js: -------------------------------------------------------------------------------- 1 | 2 | export function connected() { 3 | return {type: 'SOCKET_CONNECTED'}; 4 | } 5 | 6 | export function disconnected() { 7 | return {type: 'SOCKET_DISCONNECTED'}; 8 | } 9 | 10 | export function messageReceived(msg) { 11 | if (msg.IsNewMessage) { 12 | return { 13 | type: 'CONVERSATIONS_UNREAD_COUNT_UPDATE', 14 | data: parseInt(msg.UnreadMessages, 0) 15 | }; 16 | } 17 | 18 | if (msg.IsNewComment) { 19 | return { 20 | type: 'WATCHED_UNREAD_COUNT_UPDATE', 21 | data: parseInt(msg.UnreadPosts, 0) 22 | }; 23 | } 24 | 25 | return {type: 'SOCKET_CONNECTED'}; 26 | } 27 | -------------------------------------------------------------------------------- /src/redux/core/ws/reducer.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | connectionStatus: 'DISCONNECTED' 3 | }; 4 | 5 | export default function socketReducer(state = initialState, action) { 6 | switch (action.type) { 7 | case 'SOCKET_CONNECTED': 8 | return { 9 | ...state, 10 | connectionStatus: 'CONNECTED' 11 | }; 12 | case 'SOCKET_DISCONNECTED': 13 | return { 14 | ...state, 15 | connectionStatus: 'DISCONNECTED' 16 | }; 17 | default: 18 | return state; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/redux/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Combine All Reducers 3 | */ 4 | 5 | import {combineReducers} from 'redux'; 6 | 7 | // Our custom reducers 8 | // We need to import each one here and add them to the combiner at the bottom 9 | import router from '@redux/core/router/reducer'; 10 | import user from '@redux/core/user/reducer'; 11 | import stream from '@redux/core/stream/reducer'; 12 | import conversations from '@redux/core//conversations/reducer'; 13 | import systemNotifications from '@redux/core/system-notifications/reducer'; 14 | import socket from '@redux/core/ws/reducer'; 15 | 16 | // Combine all 17 | const appReducer = combineReducers({ 18 | router, 19 | user, 20 | stream, 21 | conversations, 22 | systemNotifications, 23 | socket 24 | }); 25 | 26 | // Setup root reducer 27 | const rootReducer = (state, action) => { 28 | const newState = (action.type === 'RESET') ? null : state; 29 | return appReducer(newState, action); 30 | }; 31 | 32 | export default rootReducer; 33 | -------------------------------------------------------------------------------- /src/redux/middleware/analyticsMiddleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Redux Middleware to track Redux Actions 3 | */ 4 | import {GoogleAnalyticsTracker} from 'react-native-google-analytics-bridge'; 5 | 6 | // Consts and Libs 7 | import {AppConfig} from '@constants/'; 8 | 9 | // Google Analytics 10 | const GoogleAnalytics = new GoogleAnalyticsTracker(AppConfig.gaTrackingId); 11 | 12 | // eslint-disable-next-line no-unused-vars 13 | const analyticsMiddleware = store => next => (action) => { 14 | // Track each screen view to Redux 15 | // - Requires that each Scene in RNRF have a 'analyticsDesc' prop 16 | switch (action.type) { 17 | case 'REACT_NATIVE_ROUTER_FLUX_FOCUS': 18 | if (action && action.scene && action.scene.analyticsDesc) { 19 | try { 20 | const screenName = (action.scene.title) 21 | ? `${action.scene.analyticsDesc} - ${action.scene.title}` 22 | : action.scene.analyticsDesc; 23 | 24 | // Send to Google Analytics 25 | GoogleAnalytics.trackScreenView(screenName); 26 | } catch (err) { 27 | console.log(err); 28 | } 29 | } 30 | break; 31 | 32 | default: 33 | } 34 | return next(action); 35 | }; 36 | 37 | export default analyticsMiddleware; 38 | -------------------------------------------------------------------------------- /src/redux/middleware/apiMiddleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Middleware for handling api responses 3 | */ 4 | 5 | const apiMiddleware = store => next => (action) => { 6 | switch (action.type) { 7 | case 'API_SUCCESS_RESPONSE': 8 | // Update badges count by x-badges header 9 | if (action.headers && action.headers.has('x-badges')) { 10 | // parse x-badges header 11 | // can be parse with /([a-z,A-Z]\d+)/g too 12 | const mVar = action.headers.get('x-badges').match(/([m]\d+)/g)[0].replace('m', ''); 13 | const wVar = action.headers.get('x-badges').match(/([w]\d+)/g)[0].replace('w', ''); 14 | const sVar = action.headers.get('x-badges').match(/([S]\d+)/g)[0].replace('S', ''); 15 | 16 | // get current state 17 | const state = store.getState(); 18 | 19 | // call reducer if counts changed 20 | 21 | if (state.conversations.UnreadCount !== mVar) { 22 | store.dispatch({ 23 | type: 'CONVERSATIONS_UNREAD_COUNT_UPDATE', 24 | data: parseInt(mVar, 0) 25 | }); 26 | } 27 | 28 | if (state.stream.UnreadCount !== wVar) { 29 | store.dispatch({ 30 | type: 'WATCHED_UNREAD_COUNT_UPDATE', 31 | data: parseInt(wVar, 0) 32 | }); 33 | } 34 | 35 | if (state.systemNotifications.UnreadCount !== sVar) { 36 | store.dispatch({ 37 | type: 'SYSTEM_NOTIFICATIONS_UNREAD_COUNT_UPDATE', 38 | data: parseInt(sVar, 0) 39 | }); 40 | } 41 | } 42 | break; 43 | 44 | default: 45 | } 46 | return next(action); 47 | }; 48 | 49 | export default apiMiddleware; 50 | -------------------------------------------------------------------------------- /src/redux/middleware/index.js: -------------------------------------------------------------------------------- 1 | import AnalyticsMiddleware from '@redux/middleware/analyticsMiddleware'; 2 | import socketMiddleware from '@redux/middleware/socketMiddleware'; 3 | import apiMiddleware from '@redux/middleware/apiMiddleware'; 4 | 5 | export {AnalyticsMiddleware, socketMiddleware, apiMiddleware}; 6 | -------------------------------------------------------------------------------- /src/redux/middleware/socketMiddleware.js: -------------------------------------------------------------------------------- 1 | // Websocket 2 | import WebSocket from 'reconnecting-websocket'; 3 | 4 | // Actions 5 | import * as actions from '@redux/core/ws/actions'; 6 | 7 | const socketMiddleware = (() => { 8 | let socket = null; 9 | 10 | const onOpen = (ws, store, token) => () => { 11 | // Send authenticate with remote 12 | ws.send(`token=${token}`); 13 | // Tell the store we're connected 14 | store.dispatch(actions.connected()); 15 | }; 16 | 17 | const onClose = (ws, store) => () => { 18 | // Tell the store we've disconnected 19 | store.dispatch(actions.disconnected()); 20 | }; 21 | 22 | const onMessage = (ws, store) => (evt) => { 23 | // Parse the JSON message received on the websocket 24 | const msg = JSON.parse(evt.data); 25 | 26 | console.log(msg); 27 | // handle actions 28 | store.dispatch(actions.messageReceived(msg)); 29 | }; 30 | 31 | return store => next => (action) => { 32 | switch (action.type) { 33 | case 'CONNECT': 34 | // Start a new connection to the server 35 | if (socket !== null) { 36 | socket.close(); 37 | } 38 | 39 | socket = new WebSocket('wss://www.wnmlive.com/mobile-ws.ashx'); 40 | socket.onmessage = onMessage(socket, store); 41 | socket.onclose = onClose(socket, store); 42 | socket.onopen = onOpen(socket, store, action.token); 43 | 44 | break; 45 | 46 | // The user wants us to disconnect 47 | case 'DISCONNECT': 48 | if (socket !== null) { 49 | socket.close(); 50 | } 51 | socket = null; 52 | 53 | // Set our state to disconnected 54 | store.dispatch(actions.disconnected()); 55 | break; 56 | 57 | // This action is irrelevant to us, pass it on to the next middleware 58 | default: 59 | return next(action); 60 | } 61 | 62 | return next(action); 63 | }; 64 | })(); 65 | 66 | export default socketMiddleware; 67 | -------------------------------------------------------------------------------- /src/redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@redux" 3 | } 4 | -------------------------------------------------------------------------------- /src/theme/colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Theme - Colors 3 | */ 4 | 5 | const app = { 6 | background: '#FFFFFF', 7 | streamBackground: '#E9EBEE', 8 | cardBackground: '#FFFFFF', 9 | listItemBackground: '#FFFFFF' 10 | }; 11 | 12 | const brand = { 13 | brand: { 14 | primary: '#232F3A', 15 | secondary: '#818F92' 16 | } 17 | }; 18 | 19 | const text = { 20 | textPrimary: '#222222', 21 | textSecondary: '#535353', 22 | textCard: '#222222', 23 | headingPrimary: brand.brand.primary, 24 | headingSecondary: brand.brand.primary 25 | }; 26 | 27 | const borders = { 28 | border: '#dedfe3' 29 | }; 30 | 31 | const tabbar = { 32 | tabbar: { 33 | background: '#232F3A', 34 | iconDefault: '#818F92', 35 | iconSelected: '#FFFFFF', 36 | iconNew: '#008DCB' 37 | }, 38 | tabbarTop: { 39 | background: '#232F3A', 40 | indicator: '#CB0000', 41 | icon: '#D0E1F9' 42 | } 43 | }; 44 | 45 | const segment = { 46 | segmentButton: { 47 | selectedTextColor: '#364150', 48 | textColor: '#364150', 49 | background: '#FCFCFA', 50 | selectedBackground: '#dedfe3', 51 | borderColor: '#b3c1c4' 52 | } 53 | }; 54 | 55 | export default { 56 | ...app, 57 | ...brand, 58 | ...text, 59 | ...borders, 60 | ...tabbar, 61 | ...segment 62 | }; 63 | -------------------------------------------------------------------------------- /src/theme/fonts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Theme - Fonts 3 | */ 4 | import {Platform} from 'react-native'; 5 | 6 | import Sizes from './sizes'; 7 | 8 | const guidelineBaseWidth = 350; 9 | 10 | const scale = size => Sizes.screen.width / guidelineBaseWidth * size; 11 | 12 | const base = { 13 | size: scale(18), 14 | lineHeight: 18, 15 | ...Platform.select({ 16 | ios: { 17 | family: 'Roboto-Regular', 18 | familyLight: 'Roboto-Light', 19 | familyBold: 'Roboto-Medium' 20 | }, 21 | android: { 22 | family: 'Roboto-Regular', 23 | familyLight: 'Roboto-Light', 24 | familyBold: 'Roboto-Medium' 25 | } 26 | }) 27 | }; 28 | 29 | export default { 30 | base: {...base}, 31 | subtext: {size: scale(13), family: base.familyLight}, 32 | p: {...base, size: scale(15), family: base.familyLight}, 33 | h1: {...base, size: scale(26), family: base.familyBold}, 34 | h2: {...base, size: scale(24), family: base.familyBold}, 35 | h3: {...base, size: scale(20), family: base.familyBold}, 36 | h4: {...base, size: scale(18), family: base.familyBold}, 37 | h5: {...base, size: scale(15), family: base.familyBold} 38 | }; 39 | -------------------------------------------------------------------------------- /src/theme/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Theme 3 | */ 4 | 5 | import AppColors from './colors'; 6 | import AppFonts from './fonts'; 7 | import AppStyles from './styles'; 8 | import AppSizes from './sizes'; 9 | 10 | export {AppColors, AppFonts, AppStyles, AppSizes}; 11 | -------------------------------------------------------------------------------- /src/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@theme" 3 | } 4 | -------------------------------------------------------------------------------- /src/theme/sizes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Theme - Sizes 3 | */ 4 | import {Dimensions, Platform} from 'react-native'; 5 | 6 | const {width, height} = Dimensions.get('window'); 7 | const screenHeight = width < height ? height : width; 8 | const screenWidth = width < height ? width : height; 9 | 10 | export default { 11 | // Window Dimensions 12 | screen: { 13 | height: screenHeight, 14 | width: screenWidth, 15 | 16 | widthHalf: screenWidth * 0.5, 17 | widthThird: screenWidth * 0.333, 18 | widthTwoThirds: screenWidth * 0.666, 19 | widthQuarter: screenWidth * 0.25, 20 | widthThreeQuarters: screenWidth * 0.75 21 | }, 22 | navbarHeight: (Platform.OS === 'ios') ? 60 : 50, 23 | statusBarHeight: (Platform.OS === 'ios') ? 16 : 0, 24 | tabbarHeight: 50, 25 | 26 | padding: 20, 27 | paddingSml: 10, 28 | 29 | borderRadius: 8 30 | }; 31 | --------------------------------------------------------------------------------