├── .buckconfig ├── .eslintrc.json ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── App.js ├── README.MD ├── __tests__ └── App-test.js ├── android ├── app │ ├── BUCK │ ├── build.gradle │ ├── build_defs.bzl │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── fonts │ │ │ ├── AntDesign.ttf │ │ │ ├── Entypo.ttf │ │ │ ├── EvilIcons.ttf │ │ │ ├── Feather.ttf │ │ │ ├── FontAwesome.ttf │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ ├── Foundation.ttf │ │ │ ├── Ionicons.ttf │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ ├── MaterialIcons.ttf │ │ │ ├── Octicons.ttf │ │ │ ├── SimpleLineIcons.ttf │ │ │ └── Zocial.ttf │ │ ├── java │ │ └── com │ │ │ └── reactnativetemplate │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.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 ├── babel.config.js ├── index.js ├── ios ├── ReactNativeTemplate-tvOS │ └── Info.plist ├── ReactNativeTemplate-tvOSTests │ └── Info.plist ├── ReactNativeTemplate.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── ReactNativeTemplate-tvOS.xcscheme │ │ └── ReactNativeTemplate.xcscheme ├── ReactNativeTemplate │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Info.plist │ └── main.m └── ReactNativeTemplateTests │ ├── Info.plist │ └── ReactNativeTemplateTests.m ├── metro.config.js ├── package-lock.json ├── package.json ├── src ├── assets │ └── images │ │ ├── AcceptIcon.png │ │ ├── Add.png │ │ ├── AddIcon.png │ │ ├── AddMemberIcon.png │ │ ├── AssignasAdminIcon.png │ │ ├── BackArrow.png │ │ ├── BackArrowBlack.png │ │ ├── CameraIcon.png │ │ ├── ChatIcon.png │ │ ├── ChatIconHighlighted.png │ │ ├── CloseIcon.png │ │ ├── DeleteIcon.png │ │ ├── DenyIcon.png │ │ ├── EditIconPencil.png │ │ ├── EmptyResourceImage.png │ │ ├── EmptyResourceLarge.png │ │ ├── EndOfPosts.png │ │ ├── Eye-Off.png │ │ ├── Eye-On.png │ │ ├── EyeIcon.png │ │ ├── GrayAdd.png │ │ ├── GroupAdminIcon.png │ │ ├── GroupDefault.png │ │ ├── HomeIcon.png │ │ ├── HomeIconFilled.png │ │ ├── ImagePlaceholder.png │ │ ├── LeaveGroupIcon.png │ │ ├── LikeIcon.png │ │ ├── LikeIconHighlighted.png │ │ ├── MessageIcon.png │ │ ├── MessageIconFilled.png │ │ ├── MessageMemberIcon.png │ │ ├── MoreIcon.png │ │ ├── MoreIconFilled.png │ │ ├── NewGroupIcon.png │ │ ├── NewPostIcon.png │ │ ├── NotificationIcon.png │ │ ├── NotificationIconFilled.png │ │ ├── People.png │ │ ├── PeopleGreen.png │ │ ├── PhotoLibraryIcon.png │ │ ├── RemovePhoto.png │ │ ├── RightArrow.png │ │ ├── SettingsIcon.png │ │ ├── SingleUserIcon.png │ │ ├── SmallArrowDown.png │ │ ├── Success.png │ │ ├── SuccessBlackRing.png │ │ ├── arrowRightWhite.png │ │ ├── asianjim.jpg │ │ ├── checkmark.png │ │ ├── close.zip │ │ ├── logo.png │ │ ├── orangeArrow.png │ │ └── pam-beesly.jpg ├── components │ ├── AboutCard.js │ ├── ActionSheet.js │ ├── ActionSheetItem.js │ ├── AllCaughtUpCard.js │ ├── AutoGrowTextField.js │ ├── Avatar.js │ ├── BottomActionButton.js │ ├── BottomOfPostsCard.js │ ├── CommentCard.js │ ├── CommentReactionItem.js │ ├── FeedCard.js │ ├── FeedCardMainImage.js │ ├── Global │ │ └── colors.js │ ├── Header.js │ ├── HeaderDetail.js │ ├── Icon.js │ ├── ImagePicker.js │ ├── ImagePreview.js │ ├── Label.js │ ├── LargeButton.js │ ├── LayoutScrollViewWithHeader.js │ ├── LikeReactionItem.js │ ├── LoadMoreCard.js │ ├── LogoIcon.js │ ├── NewComment.js │ ├── NothingHereYetCard.js │ ├── PagingView.js │ ├── PickerItem.js │ ├── PickerModal.js │ ├── ProfileItemCard.js │ ├── ReactionTabBar.js │ ├── SearchBar.js │ ├── SectionCard.js │ ├── SectionCardCentered.js │ ├── SwitchCard.js │ ├── TextButton.js │ ├── TextField.js │ ├── Timestamp.js │ └── TransparentInput.js ├── constants │ └── urls.js ├── navigation │ ├── AuthStack.js │ ├── MainFeedStack.js │ ├── MainSwitchNavigator.js │ ├── MessagesStack.js │ ├── SettingsStack.js │ └── TabNavigator.js ├── redux │ ├── actions │ │ ├── categories.actions.js │ │ ├── conversations.actions.js │ │ ├── groups.actions.js │ │ ├── loginType.actions.js │ │ ├── messageIndicator.actions.js │ │ ├── notificationIndicator.actions.js │ │ ├── notifications.actions.js │ │ ├── organizations.actions.js │ │ ├── posts.actions.js │ │ ├── resources.actions.js │ │ ├── tabBar.actions.js │ │ └── users.actions.js │ ├── api │ │ ├── categories.js │ │ ├── conversations.js │ │ ├── groups.js │ │ ├── notifications.js │ │ ├── organizations.js │ │ ├── posts.js │ │ ├── resources.js │ │ └── users.js │ └── reducers │ │ ├── categories.reducer.js │ │ ├── conversations.reducer.js │ │ ├── groups.reducer.js │ │ ├── index.js │ │ ├── loginType.reducer.js │ │ ├── messageIndicator.reducer.js │ │ ├── notificationIndicator.reducer.js │ │ ├── notifications.reducer.js │ │ ├── organizations.reducer.js │ │ ├── otherUsers.reducer.js │ │ ├── posts.reducer.js │ │ ├── resources.reducer.js │ │ ├── singlePost.reducer.js │ │ ├── tabBar.reducer.js │ │ └── users.reducer.js ├── screens │ ├── CreatePostScreen.js │ ├── EditProfileScreen.js │ ├── ForgotPasswordScreen.js │ ├── LaunchScreen.js │ ├── MainFeedScreen.js │ ├── MainScreen.js │ ├── PostShowScreen.js │ ├── ProfileScreen.js │ ├── SettingsScreen.js │ ├── SignInScreen.js │ └── SignUpScreen.js └── util │ ├── data.js │ └── helpers.js └── yarn.lock /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "rules": { 7 | "react/prop-types": 0, 8 | "react/jsx-filename-extension": 0, 9 | "class-methods-use-this": 0, 10 | "arrow-body-style": 0, 11 | "camelcase": 0 12 | }, 13 | "parser": "babel-eslint", 14 | "extends": ["airbnb", "prettier"] 15 | } 16 | -------------------------------------------------------------------------------- /.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 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | 28 | [options] 29 | emoji=true 30 | 31 | esproposal.optional_chaining=enable 32 | esproposal.nullish_coalescing=enable 33 | 34 | module.system=haste 35 | module.system.haste.use_name_reducers=true 36 | # get basename 37 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 38 | # strip .js or .js.flow suffix 39 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 40 | # strip .ios suffix 41 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 42 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 43 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 44 | module.system.haste.paths.blacklist=.*/__tests__/.* 45 | module.system.haste.paths.blacklist=.*/__mocks__/.* 46 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 47 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 48 | 49 | munge_underscores=true 50 | 51 | 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' 52 | 53 | module.file_ext=.js 54 | module.file_ext=.jsx 55 | module.file_ext=.json 56 | module.file_ext=.native.js 57 | 58 | suppress_type=$FlowIssue 59 | suppress_type=$FlowFixMe 60 | suppress_type=$FlowFixMeProps 61 | suppress_type=$FlowFixMeState 62 | 63 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 65 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 66 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 67 | 68 | [version] 69 | ^0.92.0 70 | -------------------------------------------------------------------------------- /.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 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // Redux 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | import reducers from './src/redux/reducers'; 7 | 8 | import LaunchScreen from './src/screens/LaunchScreen'; 9 | 10 | import AppContainer from './src/navigation/MainSwitchNavigator'; 11 | 12 | const createStoreWithMiddleWare = applyMiddleware(thunk)(createStore); 13 | // Create store here so there is not more than one store created 14 | export const store = createStoreWithMiddleWare(reducers); 15 | 16 | export default function App() { 17 | return ( 18 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # React Native / Redux Template 2 | 3 | An app with some basic components to give a jumping off point for RN apps. 4 | 5 | * Splash Screen / Loading state (Auto Login) 6 | * Sign Up 7 | * Sign In 8 | * Profile & Edit Profile 9 | * Settings & Edit settings 10 | * Main screen / List of cards / Detail Screen 11 | * Navigation 12 | 13 | ## Requirements 14 | 15 | For development, you will only need Node.js installed on your environement. 16 | 17 | --- 18 | 19 | ## Install 20 | 21 | $ git clone https://git.cratebind.com/mobile-ios/react-native-template.git 22 | $ cd react-native-template 23 | $ npm install 24 | $ react-native link 25 | 26 | ## Start & watch 27 | 28 | $ react-native run-ios 29 | 30 | --- 31 | 32 | ## To Use 33 | 34 | Create new project with 35 | 36 | $ react-native init [new-project-name] 37 | 38 | You do need some dependencies - 39 | 40 | 1. react-navigation 41 | 2. react-native-gesture-handler 42 | 3. redux 43 | 4. react-redux 44 | 5. redux-thunk 45 | 6. styled-components 46 | 7. react-native-image-picker 47 | 8. react-native-image-resizer 48 | 9. react-native-sensitive-info 49 | 10. react-native-vector-icons 50 | 11. moment 51 | 12. react-native-masked-text 52 | 53 | Link up libraries 54 | 55 | $ react-native link 56 | 57 | #### Copy src folder over into new project & replace App.js. 58 | 59 | ## May the odds be ever in your favor. 60 | 61 | -------------------------------------------------------------------------------- /__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /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 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.reactnativetemplate", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.reactnativetemplate", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/AntDesign.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/AntDesign.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativetemplate/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnativetemplate; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "ReactNativeTemplate"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativetemplate/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnativetemplate; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; 7 | import com.oblador.vectoricons.VectorIconsPackage; 8 | import br.com.classapp.RNSensitiveInfo.RNSensitiveInfoPackage; 9 | import fr.bamlab.rnimageresizer.ImageResizerPackage; 10 | import com.imagepicker.ImagePickerPackage; 11 | import com.facebook.react.ReactNativeHost; 12 | import com.facebook.react.ReactPackage; 13 | import com.facebook.react.shell.MainReactPackage; 14 | import com.facebook.soloader.SoLoader; 15 | 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | public class MainApplication extends Application implements ReactApplication { 20 | 21 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 22 | @Override 23 | public boolean getUseDeveloperSupport() { 24 | return BuildConfig.DEBUG; 25 | } 26 | 27 | @Override 28 | protected List getPackages() { 29 | return Arrays.asList( 30 | new MainReactPackage(), 31 | new RNGestureHandlerPackage(), 32 | new VectorIconsPackage(), 33 | new RNSensitiveInfoPackage(), 34 | new ImageResizerPackage(), 35 | new ImagePickerPackage() 36 | ); 37 | } 38 | 39 | @Override 40 | protected String getJSMainModuleName() { 41 | return "index"; 42 | } 43 | }; 44 | 45 | @Override 46 | public ReactNativeHost getReactNativeHost() { 47 | return mReactNativeHost; 48 | } 49 | 50 | @Override 51 | public void onCreate() { 52 | super.onCreate(); 53 | SoLoader.init(this, /* native exopackage */ false); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactNativeTemplate 3 | 4 | -------------------------------------------------------------------------------- /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 | ext { 5 | buildToolsVersion = "28.0.3" 6 | minSdkVersion = 16 7 | compileSdkVersion = 28 8 | targetSdkVersion = 28 9 | supportLibVersion = "28.0.0" 10 | } 11 | repositories { 12 | google() 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath 'com.android.tools.build:gradle:3.3.1' 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | mavenLocal() 26 | google() 27 | jcenter() 28 | maven { 29 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 30 | url "$rootDir/../node_modules/react-native/android" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/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-4.10.2-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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /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 = 'ReactNativeTemplate' 2 | include ':react-native-gesture-handler' 3 | project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') 4 | include ':react-native-vector-icons' 5 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 6 | include ':react-native-sensitive-info' 7 | project(':react-native-sensitive-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sensitive-info/android') 8 | include ':react-native-image-resizer' 9 | project(':react-native-image-resizer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-resizer/android') 10 | include ':react-native-image-picker' 11 | project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android') 12 | 13 | include ':app' 14 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactNativeTemplate", 3 | "displayName": "ReactNativeTemplate" 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplate-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/ReactNativeTemplate-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/ReactNativeTemplate.xcodeproj/xcshareddata/xcschemes/ReactNativeTemplate-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/ReactNativeTemplate.xcodeproj/xcshareddata/xcschemes/ReactNativeTemplate.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/ReactNativeTemplate/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplate/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 19 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 20 | moduleName:@"ReactNativeTemplate" 21 | initialProperties:nil]; 22 | 23 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 24 | 25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 26 | UIViewController *rootViewController = [UIViewController new]; 27 | rootViewController.view = rootView; 28 | self.window.rootViewController = rootViewController; 29 | [self.window makeKeyAndVisible]; 30 | return YES; 31 | } 32 | 33 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 34 | { 35 | #if DEBUG 36 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 37 | #else 38 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 39 | #endif 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplate/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/ReactNativeTemplate/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/ReactNativeTemplate/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplate/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ReactNativeTemplate 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 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationWhenInUseUsageDescription 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UIViewControllerBasedStatusBarAppearance 42 | 43 | NSAppTransportSecurity 44 | 45 | NSAllowsArbitraryLoads 46 | 47 | NSExceptionDomains 48 | 49 | localhost 50 | 51 | NSExceptionAllowsInsecureHTTPLoads 52 | 53 | 54 | 55 | 56 | UIAppFonts 57 | 58 | AntDesign.ttf 59 | Entypo.ttf 60 | EvilIcons.ttf 61 | Feather.ttf 62 | FontAwesome.ttf 63 | FontAwesome5_Brands.ttf 64 | FontAwesome5_Regular.ttf 65 | FontAwesome5_Solid.ttf 66 | Foundation.ttf 67 | Ionicons.ttf 68 | MaterialCommunityIcons.ttf 69 | MaterialIcons.ttf 70 | Octicons.ttf 71 | SimpleLineIcons.ttf 72 | Zocial.ttf 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplate/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios/ReactNativeTemplateTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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/ReactNativeTemplateTests/ReactNativeTemplateTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 16 | 17 | @interface ReactNativeTemplateTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation ReactNativeTemplateTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 44 | if (level >= RCTLogLevelError) { 45 | redboxError = message; 46 | } 47 | }); 48 | 49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 52 | 53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | } 60 | 61 | RCTSetLogFunction(RCTDefaultLogFunction); 62 | 63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactNativeTemplate", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "moment": "^2.24.0", 11 | "react": "16.8.3", 12 | "react-native": "0.59.5", 13 | "react-native-gesture-handler": "^1.2.1", 14 | "react-native-image-picker": "^0.28.1", 15 | "react-native-image-resizer": "^1.0.1", 16 | "react-native-masked-text": "^1.12.2", 17 | "react-native-sensitive-info": "^5.4.1", 18 | "react-native-vector-icons": "^6.4.2", 19 | "react-navigation": "^3.9.1", 20 | "react-redux": "^7.0.3", 21 | "redux": "^4.0.1", 22 | "redux-thunk": "^2.3.0", 23 | "styled-components": "^4.2.0" 24 | }, 25 | "devDependencies": { 26 | "babel-eslint": "^10.0.1", 27 | "eslint": "^5.16.0", 28 | "eslint-config-airbnb": "^17.1.0", 29 | "eslint-config-prettier": "^3.5.0", 30 | "eslint-plugin-import": "^2.14.0", 31 | "eslint-plugin-jsx-a11y": "^6.1.2", 32 | "eslint-plugin-prettier": "^3.0.1", 33 | "eslint-plugin-react": "^7.11.1", 34 | "@babel/core": "^7.4.4", 35 | "@babel/runtime": "^7.4.4", 36 | "babel-jest": "^24.8.0", 37 | "jest": "^24.8.0", 38 | "metro-react-native-babel-preset": "^0.54.0", 39 | "react-test-renderer": "16.8.3" 40 | }, 41 | "jest": { 42 | "preset": "react-native" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/assets/images/AcceptIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/AcceptIcon.png -------------------------------------------------------------------------------- /src/assets/images/Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/Add.png -------------------------------------------------------------------------------- /src/assets/images/AddIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/AddIcon.png -------------------------------------------------------------------------------- /src/assets/images/AddMemberIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/AddMemberIcon.png -------------------------------------------------------------------------------- /src/assets/images/AssignasAdminIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/AssignasAdminIcon.png -------------------------------------------------------------------------------- /src/assets/images/BackArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/BackArrow.png -------------------------------------------------------------------------------- /src/assets/images/BackArrowBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/BackArrowBlack.png -------------------------------------------------------------------------------- /src/assets/images/CameraIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/CameraIcon.png -------------------------------------------------------------------------------- /src/assets/images/ChatIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/ChatIcon.png -------------------------------------------------------------------------------- /src/assets/images/ChatIconHighlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/ChatIconHighlighted.png -------------------------------------------------------------------------------- /src/assets/images/CloseIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/CloseIcon.png -------------------------------------------------------------------------------- /src/assets/images/DeleteIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/DeleteIcon.png -------------------------------------------------------------------------------- /src/assets/images/DenyIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/DenyIcon.png -------------------------------------------------------------------------------- /src/assets/images/EditIconPencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/EditIconPencil.png -------------------------------------------------------------------------------- /src/assets/images/EmptyResourceImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/EmptyResourceImage.png -------------------------------------------------------------------------------- /src/assets/images/EmptyResourceLarge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/EmptyResourceLarge.png -------------------------------------------------------------------------------- /src/assets/images/EndOfPosts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/EndOfPosts.png -------------------------------------------------------------------------------- /src/assets/images/Eye-Off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/Eye-Off.png -------------------------------------------------------------------------------- /src/assets/images/Eye-On.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/Eye-On.png -------------------------------------------------------------------------------- /src/assets/images/EyeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/EyeIcon.png -------------------------------------------------------------------------------- /src/assets/images/GrayAdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/GrayAdd.png -------------------------------------------------------------------------------- /src/assets/images/GroupAdminIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/GroupAdminIcon.png -------------------------------------------------------------------------------- /src/assets/images/GroupDefault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/GroupDefault.png -------------------------------------------------------------------------------- /src/assets/images/HomeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/HomeIcon.png -------------------------------------------------------------------------------- /src/assets/images/HomeIconFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/HomeIconFilled.png -------------------------------------------------------------------------------- /src/assets/images/ImagePlaceholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/ImagePlaceholder.png -------------------------------------------------------------------------------- /src/assets/images/LeaveGroupIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/LeaveGroupIcon.png -------------------------------------------------------------------------------- /src/assets/images/LikeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/LikeIcon.png -------------------------------------------------------------------------------- /src/assets/images/LikeIconHighlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/LikeIconHighlighted.png -------------------------------------------------------------------------------- /src/assets/images/MessageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/MessageIcon.png -------------------------------------------------------------------------------- /src/assets/images/MessageIconFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/MessageIconFilled.png -------------------------------------------------------------------------------- /src/assets/images/MessageMemberIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/MessageMemberIcon.png -------------------------------------------------------------------------------- /src/assets/images/MoreIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/MoreIcon.png -------------------------------------------------------------------------------- /src/assets/images/MoreIconFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/MoreIconFilled.png -------------------------------------------------------------------------------- /src/assets/images/NewGroupIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/NewGroupIcon.png -------------------------------------------------------------------------------- /src/assets/images/NewPostIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/NewPostIcon.png -------------------------------------------------------------------------------- /src/assets/images/NotificationIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/NotificationIcon.png -------------------------------------------------------------------------------- /src/assets/images/NotificationIconFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/NotificationIconFilled.png -------------------------------------------------------------------------------- /src/assets/images/People.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/People.png -------------------------------------------------------------------------------- /src/assets/images/PeopleGreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/PeopleGreen.png -------------------------------------------------------------------------------- /src/assets/images/PhotoLibraryIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/PhotoLibraryIcon.png -------------------------------------------------------------------------------- /src/assets/images/RemovePhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/RemovePhoto.png -------------------------------------------------------------------------------- /src/assets/images/RightArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/RightArrow.png -------------------------------------------------------------------------------- /src/assets/images/SettingsIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/SettingsIcon.png -------------------------------------------------------------------------------- /src/assets/images/SingleUserIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/SingleUserIcon.png -------------------------------------------------------------------------------- /src/assets/images/SmallArrowDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/SmallArrowDown.png -------------------------------------------------------------------------------- /src/assets/images/Success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/Success.png -------------------------------------------------------------------------------- /src/assets/images/SuccessBlackRing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/SuccessBlackRing.png -------------------------------------------------------------------------------- /src/assets/images/arrowRightWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/arrowRightWhite.png -------------------------------------------------------------------------------- /src/assets/images/asianjim.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/asianjim.jpg -------------------------------------------------------------------------------- /src/assets/images/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/checkmark.png -------------------------------------------------------------------------------- /src/assets/images/close.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/close.zip -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/orangeArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/orangeArrow.png -------------------------------------------------------------------------------- /src/assets/images/pam-beesly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekahlbaker/react-native-app-template/c91f2f35cfeff1d914aad2d9dfcf2fb9ce16cbc8/src/assets/images/pam-beesly.jpg -------------------------------------------------------------------------------- /src/components/AboutCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import colors from './Global/colors'; 4 | 5 | const StyledMainView = styled.View` 6 | padding-left: 7; 7 | padding-bottom: 17; 8 | padding-right: 10; 9 | `; 10 | 11 | const StyledText = styled.Text` 12 | font-style: normal; 13 | font-weight: 600; 14 | line-height: 24px; 15 | font-size: 12px; 16 | letter-spacing: -0.0861539px; 17 | color: ${colors.mediumNeutral}; 18 | `; 19 | 20 | export default (AboutCard = ({ info }) => { 21 | return ( 22 | 23 | {info.map( 24 | (value, index) => 25 | !!value && 26 | value !== ', ' && 27 | value !== ', , ' && {value}, 28 | )} 29 | 30 | ); 31 | }); 32 | -------------------------------------------------------------------------------- /src/components/ActionSheet.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Modal, Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import ActionSheetItem from './ActionSheetItem'; 5 | 6 | import colors from './Global/colors'; 7 | 8 | const DEVICE_WIDTH = Dimensions.get('window').width; 9 | const DEVICE_HEIGHT = Dimensions.get('window').height; 10 | 11 | const StyledView = styled.View``; 12 | 13 | const StyledTopView = styled.TouchableOpacity` 14 | height: ${props => props.height}; 15 | background-color: transparent; 16 | `; 17 | 18 | const StyledActionSheet = styled.View` 19 | background-color: ${colors.white}; 20 | border-top-left-radius: 15; 21 | border-top-right-radius: 15; 22 | `; 23 | 24 | const StyledTitle = styled.Text` 25 | color: ${colors.black}; 26 | line-height: 20px; 27 | font-size: 22px; 28 | letter-spacing: -0.0861539px; 29 | padding-top: 28; 30 | padding-bottom: 28; 31 | padding-left: 20; 32 | `; 33 | 34 | const StyledDescription = styled.Text` 35 | line-height: 26px; 36 | font-size: 16px; 37 | text-align: center; 38 | letter-spacing: -0.0861539px; 39 | padding-bottom: 8; 40 | `; 41 | 42 | const StyledCancelButton = styled.TouchableOpacity` 43 | width: ${DEVICE_WIDTH}; 44 | background-color: white; 45 | padding-top: 24; 46 | padding-bottom: 34; 47 | `; 48 | 49 | const StyledCancel = styled.Text` 50 | align-self: center; 51 | color: ${colors.mediumAccent}; 52 | font-weight: 600; 53 | line-height: 18px; 54 | font-size: 18px; 55 | letter-spacing: -0.0861539px; 56 | `; 57 | 58 | class ActionSheet extends Component { 59 | constructor() { 60 | super(); 61 | this.state = { 62 | height: DEVICE_HEIGHT - 100, 63 | }; 64 | } 65 | 66 | calculateHeight = e => { 67 | const height = e.nativeEvent.layout.height; 68 | this.setState({ height: DEVICE_HEIGHT - height }); 69 | }; 70 | 71 | render() { 72 | return ( 73 | 78 | 79 | this.props.closeModal()} 82 | /> 83 | (this.actionSheet = ref)} 85 | onLayout={e => { 86 | this.calculateHeight(e); 87 | }} 88 | > 89 | {this.props.title} 90 | {this.props.description && ( 91 | {this.props.description} 92 | )} 93 | {this.props.actions.map((value, index) => ( 94 | 100 | ))} 101 | this.props.closeModal()}> 102 | Cancel 103 | 104 | 105 | 106 | 107 | ); 108 | } 109 | } 110 | 111 | export default ActionSheet; 112 | -------------------------------------------------------------------------------- /src/components/ActionSheetItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const StyledRowItem = styled.TouchableOpacity` 9 | width: ${DEVICE_WIDTH}; 10 | background-color: white; 11 | padding-top: 12; 12 | padding-bottom: 12; 13 | border-bottom-width: 2; 14 | border-color: ${colors.background}; 15 | flex-direction: row; 16 | align-items: center; 17 | justify-content: flex-start; 18 | margin-left: 20; 19 | height: 73; 20 | `; 21 | 22 | const StyledText = styled.Text` 23 | color: ${colors.darkNeutral}; 24 | padding-left: 10; 25 | font-weight: 600; 26 | font-size: 18px; 27 | letter-spacing: -0.0861539px; 28 | `; 29 | 30 | const StyledIcon = styled.Image``; 31 | 32 | export default (actionSheetItem = ({ onPress, icon, title }) => ( 33 | onPress()}> 34 | 35 | {title} 36 | 37 | )); 38 | -------------------------------------------------------------------------------- /src/components/AllCaughtUpCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const StyledMainView = styled.View` 9 | width: ${DEVICE_WIDTH - 20}; 10 | margin-top: 20; 11 | margin-bottom: 20; 12 | align-items: center; 13 | justify-content: center; 14 | `; 15 | 16 | const StyledRowView = styled.View` 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | `; 21 | 22 | const StyledLineView = styled.View` 23 | height: 2; 24 | background-color: ${colors.lightNeutral}; 25 | flex: 1; 26 | margin-left: 10; 27 | margin-right: 10; 28 | `; 29 | 30 | const StyledText = styled.Text` 31 | font-weight: 600; 32 | line-height: 26px; 33 | font-size: 16px; 34 | text-align: center; 35 | letter-spacing: -0.0861539px; 36 | color: ${colors.mediumNeutral}; 37 | margin-left: 10; 38 | margin-right: 10; 39 | `; 40 | 41 | export default (AllCaughtUpCard = ({ section, shouldShow }) => { 42 | if (section.title === 'allCaughtUp' && shouldShow) { 43 | return ( 44 | 45 | 46 | 47 | You're all caught up! 48 | 49 | 50 | 51 | ); 52 | } 53 | return null; 54 | }); 55 | -------------------------------------------------------------------------------- /src/components/AutoGrowTextField.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Keyboard } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const StyledView = styled.View` 7 | background-color: ${colors.white}; 8 | padding: 15px 10px; 9 | align-self: center; 10 | justify-content: space-between; 11 | flex-direction: row; 12 | `; 13 | 14 | const StyledTextInput = styled.TextInput` 15 | height: ${props => props.height}; 16 | flex: 1; 17 | color: ${colors.darkNeutral}; 18 | font-style: normal; 19 | font-weight: 600; 20 | font-size: 14px; 21 | letter-spacing: -0.0984616px; 22 | margin-right: 4; 23 | `; 24 | 25 | const StyledIconView = styled.TouchableOpacity` 26 | align-self: flex-end; 27 | `; 28 | 29 | const StyledSendButton = styled.Text` 30 | font-style: normal; 31 | font-weight: bold; 32 | font-size: 14px; 33 | text-align: right; 34 | letter-spacing: -0.0861539px; 35 | text-decoration-line: underline; 36 | text-decoration-color: ${props => props.color}; 37 | color: ${props => props.color}; 38 | `; 39 | 40 | class AutoGrowTextField extends Component { 41 | constructor() { 42 | super(); 43 | 44 | this.state = { 45 | height: 24, 46 | }; 47 | } 48 | 49 | updateSize = height => { 50 | if (height > 24) { 51 | this.setState({ 52 | height, 53 | }); 54 | } 55 | }; 56 | 57 | onSubmit = () => { 58 | Keyboard.dismiss(); 59 | this.props.onSubmit(); 60 | }; 61 | 62 | render() { 63 | const { 64 | viewWidth, 65 | placeholder, 66 | onChangeText, 67 | value, 68 | myRef, 69 | onFocus, 70 | shouldHaveSendButton, 71 | textColor, 72 | buttonTitle, 73 | } = this.props; 74 | return ( 75 | 76 | 85 | this.updateSize(e.nativeEvent.contentSize.height) 86 | } 87 | ref={myRef} 88 | onFocus={onFocus} 89 | blurOnSubmit 90 | /> 91 | {shouldHaveSendButton && ( 92 | this.onSubmit()}> 93 | 94 | {buttonTitle || 'Send'} 95 | 96 | 97 | )} 98 | 99 | ); 100 | } 101 | } 102 | 103 | export default AutoGrowTextField; 104 | -------------------------------------------------------------------------------- /src/components/Avatar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import colors from './Global/colors'; 4 | 5 | const StyledTouchableAvatar = styled.TouchableOpacity``; 6 | 7 | const StyledAvatar = styled.Image` 8 | width: ${props => props.size}; 9 | height: ${props => props.size}; 10 | border-radius: ${props => props.size / 2}; 11 | `; 12 | 13 | const StyledEmptyProfile = styled.View` 14 | width: ${props => props.size}; 15 | height: ${props => props.size}; 16 | border-radius: ${props => props.size / 2}; 17 | background-color: ${colors.mediumNeutral}; 18 | justify-content: center; 19 | align-items: center; 20 | `; 21 | 22 | const StyledInitials = styled.Text` 23 | font-weight: 600; 24 | line-height: 20px; 25 | font-size: 16px; 26 | text-align: center; 27 | letter-spacing: -0.0984616px; 28 | text-transform: uppercase; 29 | color: ${colors.background}; 30 | padding-top: 1; 31 | `; 32 | 33 | export default (Avatar = props => { 34 | // console.log('AVATAR PROPS ', props); 35 | const { source, firstName, lastName, onSelectUser, size } = props; 36 | let useInitials = false; 37 | if ( 38 | source === 39 | 'https://church-groups.cratebind.com/avatars/original/missing.png' 40 | ) { 41 | useInitials = true; 42 | } 43 | if ( 44 | source === 'https://church-groups.cratebind.com/logos/original/missing.png' 45 | ) { 46 | useInitials = true; 47 | } 48 | const firstInitial = firstName.substring(0, 1); 49 | const lastInitial = lastName.substring(0, 1); 50 | const avatar = useInitials ? ( 51 | 52 | 53 | {`${firstInitial}${lastInitial}`} 54 | 55 | 56 | ) : ( 57 | 58 | 59 | 60 | ); 61 | return avatar; 62 | }); 63 | -------------------------------------------------------------------------------- /src/components/BottomActionButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Platform, Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import { DEVICE_WIDTH } from './HeaderDetail'; 5 | import colors from '../components/Global/colors'; 6 | 7 | const StyledLargeButton = styled.TouchableOpacity` 8 | width: ${DEVICE_WIDTH} 9 | height: 84; 10 | background-color: ${props => props.background}; 11 | justify-content: center; 12 | position: absolute; 13 | bottom: 0; 14 | `; 15 | 16 | const StyledText = styled.Text` 17 | color: ${props => props.color || 'white'}; 18 | align-self: center; 19 | font-style: normal; 20 | font-weight: 600; 21 | line-height: 18px; 22 | font-size: 18px; 23 | text-align: center; 24 | letter-spacing: -0.0861539px; 25 | `; 26 | 27 | export default (BottomActionButton = props => ( 28 | 33 | {props.title} 34 | 35 | )); 36 | -------------------------------------------------------------------------------- /src/components/BottomOfPostsCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | import EndOfPosts from '../assets/images/EndOfPosts.png'; 6 | 7 | const DEVICE_WIDTH = Dimensions.get('window').width; 8 | 9 | const StyledMainView = styled.View` 10 | width: ${DEVICE_WIDTH - 20}; 11 | margin-top: 10; 12 | margin-bottom: 30; 13 | align-items: center; 14 | justify-content: center; 15 | `; 16 | 17 | const StyledText = styled.Text` 18 | font-weight: 600; 19 | line-height: 26px; 20 | font-size: 16px; 21 | text-align: center; 22 | letter-spacing: -0.0861539px; 23 | color: ${colors.mediumNeutral}; 24 | margin-top: 10; 25 | `; 26 | 27 | const StyledTextLinkButton = styled.TouchableOpacity``; 28 | 29 | const StyledTextLink = styled.Text` 30 | font-weight: 600; 31 | line-height: 26px; 32 | font-size: 16px; 33 | text-align: center; 34 | letter-spacing: -0.0861539px; 35 | color: ${colors.mediumNeutral}; 36 | text-decoration-line: underline; 37 | `; 38 | 39 | const StyledImage = styled.Image``; 40 | 41 | export default (BottomOfPostsCard = ({ onPress }) => ( 42 | 43 | 44 | You've reached the bottom of this feed! 45 | 46 | Scroll to top. 47 | 48 | 49 | )); 50 | -------------------------------------------------------------------------------- /src/components/CommentCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import moment from 'moment'; 4 | import styled from 'styled-components/native'; 5 | import Ionicons from 'react-native-vector-icons/Ionicons'; 6 | import Avatar from './Avatar'; 7 | import colors from './Global/colors'; 8 | import LikeReactionItem from './LikeReactionItem'; 9 | 10 | const DEVICE_WIDTH = Dimensions.get('window').width; 11 | 12 | const StyledMainView = styled.View` 13 | width: ${DEVICE_WIDTH - 20}; 14 | background-color: ${colors.offWhite}; 15 | margin-top: 2; 16 | `; 17 | 18 | const StyledRowView = styled.View` 19 | flex-direction: row; 20 | align-items: center; 21 | padding-left: 14; 22 | padding-bottom: 9; 23 | `; 24 | 25 | const StyledName = styled.Text` 26 | margin-left: 9; 27 | color: ${colors.darkNeutral}; 28 | font-weight: bold; 29 | line-height: 14px; 30 | font-size: 12px; 31 | letter-spacing: -0.0738462px; 32 | `; 33 | 34 | const StyledTime = styled.Text` 35 | text-align: right; 36 | font-size: 11; 37 | margin-top: 9; 38 | margin-right: 14; 39 | color: ${colors.mediumNeutral}; 40 | font-weight: 600; 41 | line-height: 11px; 42 | font-size: 9px; 43 | letter-spacing: -0.0553846px; 44 | `; 45 | 46 | const StyledContent = styled.Text` 47 | width: ${DEVICE_WIDTH - 48}; 48 | color: ${colors.darkNeutral}; 49 | font-weight: 600; 50 | line-height: 18px; 51 | font-size: 11px; 52 | letter-spacing: -0.0738462px; 53 | align-self: center; 54 | `; 55 | 56 | const StyledLikeRowView = styled.View` 57 | flex-direction: row; 58 | align-items: center; 59 | justify-content: flex-start; 60 | padding-top: 8; 61 | padding-bottom: 8; 62 | padding-left: 14; 63 | background: ${colors.offWhite}; 64 | width: ${DEVICE_WIDTH - 48}; 65 | `; 66 | 67 | const StyledButton = styled.TouchableOpacity` 68 | right: 16; 69 | top: 24; 70 | position: absolute; 71 | `; 72 | 73 | class CommentCard extends Component { 74 | constructor(props) { 75 | super(props); 76 | 77 | this.state = { 78 | likesOpen: false, 79 | userLiked: false, 80 | }; 81 | } 82 | 83 | componentDidMount() { 84 | this.determineIfUserLiked(); 85 | } 86 | 87 | componentDidUpdate(prevProps, prevState) { 88 | // console.log('Comment Card Updating ', this.props); 89 | if ( 90 | prevProps.comment.reactions.length !== this.props.comment.reactions.length 91 | ) { 92 | this.determineIfUserLiked(); 93 | } 94 | } 95 | 96 | determineIfUserLiked = () => { 97 | // filter through likes, compare with currentUser Id and set accordingly 98 | if (this.props.currentUserId) { 99 | // console.log('CURRENT USER ID ', this.props.currentUserId); 100 | const filtered = this.props.comment.reactions.filter( 101 | reaction => reaction.user.id === this.props.currentUserId, 102 | ); 103 | this.setState({ userLiked: filtered.length > 0 }); 104 | } 105 | }; 106 | 107 | onPressLike = () => { 108 | if (this.state.userLiked) { 109 | this.setState({ 110 | userLiked: false, 111 | }); 112 | this.props.onPressUnlike(this.props.comment); 113 | } else { 114 | this.setState({ 115 | userLiked: true, 116 | }); 117 | this.props.onPressLike(this.props.comment); 118 | } 119 | }; 120 | 121 | render() { 122 | // console.log('Comment Card ', this.props); 123 | const { 124 | content, 125 | created_at, 126 | post_id, 127 | reactions, 128 | user, 129 | } = this.props.comment; 130 | const timeAgo = moment(created_at).fromNow(); 131 | return ( 132 | 133 | {timeAgo} 134 | 135 | 141 | console.log('selected profile from comment card') 142 | } 143 | /> 144 | {user.full_name} 145 | 146 | {user.id === this.props.currentUserId && ( 147 | 149 | this.props.showCommentOptionModal(this.props.comment) 150 | } 151 | > 152 | 153 | 154 | )} 155 | {content} 156 | 157 | this.onPressLike()} 162 | open={this.props.likesOpen} 163 | toggleOpen={() => this.props.onPressViewLikes()} 164 | userLiked={this.state.userLiked} 165 | /> 166 | 167 | 168 | ); 169 | } 170 | } 171 | 172 | export default CommentCard; 173 | -------------------------------------------------------------------------------- /src/components/CommentReactionItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import Ionicons from 'react-native-vector-icons/Ionicons'; 4 | import ChatIcon from '../assets/images/ChatIcon.png'; 5 | import ChatIconHighlighted from '../assets/images/ChatIconHighlighted.png'; 6 | import Icon from './Icon'; 7 | import colors from './Global/colors'; 8 | 9 | const StyledRowView = styled.View` 10 | flex-direction: row; 11 | align-items: center; 12 | justify-content: center; 13 | `; 14 | 15 | const StyledReactionImage = styled.TouchableOpacity``; 16 | 17 | const StyledReactionTitle = styled.Text` 18 | color: ${colors.darkNeutral}; 19 | text-align: right; 20 | font-weight: 600; 21 | line-height: 11px; 22 | font-size: 10px; 23 | letter-spacing: -0.0553846px; 24 | margin-left: 6; 25 | margin-right: 6; 26 | `; 27 | 28 | const StyledTouchableWithoutFeedback = styled.TouchableWithoutFeedback``; 29 | 30 | function determineString(count) { 31 | switch (count) { 32 | case 0: 33 | return 'Comment'; 34 | case 1: 35 | return '1 comment'; 36 | default: 37 | return `${count} comments`; 38 | } 39 | } 40 | 41 | export default (CommentReactionItem = ({ 42 | toggleOpen, 43 | open, 44 | commentCount, 45 | onPressReaction, 46 | userCommented, 47 | }) => ( 48 | 49 | 50 | 51 | 56 | 57 | {determineString(commentCount)} 58 | 59 | 60 | 61 | 62 | 63 | )); 64 | -------------------------------------------------------------------------------- /src/components/FeedCardMainImage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | const thirds = Dimensions.get('window').height / 3; 8 | const IMAGE_HEIGHT = thirds - 50; 9 | 10 | // console.log('IMAGE HEIGHT ', IMAGE_HEIGHT); 11 | 12 | const StyledImageButton = styled.TouchableOpacity``; 13 | 14 | const StyledMainImage = styled.Image` 15 | width: ${DEVICE_WIDTH - 48}; 16 | height: ${IMAGE_HEIGHT}; 17 | align-self: center; 18 | margin-top: 13; 19 | margin-bottom: 16; 20 | `; 21 | 22 | const StyledHalfImageRowView = styled.View` 23 | flex-direction: row; 24 | align-items: center; 25 | justify-content: space-evenly; 26 | margin-top: 13; 27 | margin-bottom: 16; 28 | `; 29 | 30 | const StyledHalfImage = styled.Image` 31 | width: ${(DEVICE_WIDTH - 45) / 2}; 32 | height: ${IMAGE_HEIGHT}; 33 | `; 34 | 35 | const StyledHalfImageOverlay = styled.View` 36 | background-color: rgba(29, 30, 33, 0.5); 37 | width: ${(DEVICE_WIDTH - 45) / 2}; 38 | height: ${IMAGE_HEIGHT}; 39 | position: absolute; 40 | z-index: 1; 41 | justify-content: center; 42 | `; 43 | 44 | const StyledOverlayCount = styled.Text` 45 | color: ${colors.white}; 46 | font-weight: 600; 47 | line-height: 37px; 48 | font-size: 32px; 49 | letter-spacing: -0.196923px; 50 | align-self: center; 51 | `; 52 | 53 | export default (FeedCardMainImage = ({ images, openModal }) => { 54 | //Should all be touchable view that opens paging with all images 55 | 56 | if (images.length === 0) { 57 | return null; 58 | } else if (images.length === 1) { 59 | return ( 60 | openModal({ images: images, index: 0 })} 62 | > 63 | 64 | 65 | ); 66 | } else if (images.length === 2) { 67 | return ( 68 | 69 | openModal({ images: images, index: 0 })} 71 | > 72 | 73 | 74 | openModal({ images: images, index: 1 })} 76 | > 77 | 78 | 79 | 80 | ); 81 | } else { 82 | return ( 83 | 84 | openModal({ images: images, index: 0 })} 86 | > 87 | 88 | 89 | openModal({ images: images, index: 1 })} 91 | > 92 | 93 | {`+${images.length - 2}`} 94 | 95 | 96 | 97 | 98 | ); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /src/components/Global/colors.js: -------------------------------------------------------------------------------- 1 | export default (colors = { 2 | primary: '#52b3d9', 3 | darkerBase: '#f3f1ef', 4 | highlightSoftRed: '#FF7D6B', 5 | darkAccent: '#E05037', 6 | mediumAccent: '#FF674C', 7 | lightAccent: '#FFAA80', 8 | extraLightAccent: '#FFE4D7', 9 | white: '#FFFFFF', 10 | offWhite: '#FAFAFA', 11 | black: '#1D1E21', 12 | darkNeutral: '#414447', 13 | mediumNeutral: '#959699', 14 | lightNeutral: '#E0E0E0', 15 | background: '#F2F3F4', 16 | error: '#D70E0E', 17 | }); 18 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import SearchBar from './SearchBar'; 5 | import colors from './Global/colors'; 6 | 7 | export const DEVICE_WIDTH = Dimensions.get('window').width; 8 | 9 | export const StyledHeader = styled.View` 10 | width: ${DEVICE_WIDTH}; 11 | height: 80; 12 | background-color: ${colors.primary}; 13 | top: 0; 14 | left: 0; 15 | right: 0; 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | `; 21 | 22 | export const StyledHeaderTitle = styled.Text` 23 | font-weight: bold; 24 | font-size: 22px; 25 | text-align: center; 26 | letter-spacing: -0.0861539px; 27 | color: ${colors.white}; 28 | `; 29 | 30 | export const StyledButtonText = styled.Text` 31 | color: #fff; 32 | font-weight: bold; 33 | text-decoration: underline; 34 | text-decoration-color: #fff; 35 | `; 36 | 37 | export const StyledLeftButton = styled.TouchableOpacity` 38 | margin-left: 11; 39 | display: flex; 40 | flex-direction: row; 41 | align-items: center; 42 | justify-content: flex-start; 43 | `; 44 | 45 | const StyledRightButton = styled.TouchableOpacity` 46 | margin-right: 16; 47 | display: flex; 48 | flex-direction: row; 49 | align-items: center; 50 | justify-content: flex-end; 51 | `; 52 | 53 | const StyledSearchBarView = styled.View` 54 | border-radius: 8; 55 | align-self: center; 56 | align-self: center; 57 | justify-content: center; 58 | height: 30; 59 | background-color: ${props => props.backgroundColor}; 60 | `; 61 | 62 | export const StyledIcon = styled.Image``; 63 | 64 | export default (Header = props => { 65 | const { 66 | leftHeaderIcon, 67 | leftHeaderButtonAction, 68 | leftHeaderButtonTitle, 69 | rightHeaderIcon, 70 | rightHeaderButtonAction, 71 | rightHeaderButtonTitle, 72 | hasBar, 73 | value, 74 | placeholder, 75 | onChangeText, 76 | headerTitle, 77 | width, 78 | backgroundColor, 79 | inputColor, 80 | } = props; 81 | const hasLeftIcon = leftHeaderIcon !== undefined && leftHeaderIcon !== ''; 82 | 83 | const hasRightIcon = rightHeaderIcon !== undefined && rightHeaderIcon !== ''; 84 | 85 | return ( 86 | 87 | 88 | {hasLeftIcon && } 89 | {leftHeaderButtonTitle !== '' && ( 90 | {leftHeaderButtonTitle} 91 | )} 92 | 93 | {hasBar ? ( 94 | 95 | 103 | 104 | ) : ( 105 | {headerTitle} 106 | )} 107 | 108 | {hasRightIcon && } 109 | {rightHeaderButtonTitle !== '' && ( 110 | {rightHeaderButtonTitle} 111 | )} 112 | 113 | 114 | ); 115 | }); 116 | -------------------------------------------------------------------------------- /src/components/HeaderDetail.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | import Avatar from './Avatar'; 6 | 7 | export const DEVICE_WIDTH = Dimensions.get('window').width; 8 | 9 | const StyledHeader = styled.View` 10 | width: ${DEVICE_WIDTH}; 11 | height: 120; 12 | background-color: ${colors.white}; 13 | justify-content: center; 14 | padding-left: 10; 15 | `; 16 | 17 | const StyledBottomLine = styled.View` 18 | width: ${DEVICE_WIDTH}; 19 | height: 2; 20 | background-color: ${colors.background}; 21 | `; 22 | 23 | const StyledHeaderTitle = styled.Text` 24 | font-weight: bold; 25 | font-size: 22px; 26 | letter-spacing: -0.0861539px; 27 | color: ${colors.black}; 28 | margin-top: 23; 29 | `; 30 | 31 | const StyledRowView = styled.View` 32 | flex-direction: row; 33 | align-items: flex-end; 34 | justify-content: space-between; 35 | `; 36 | 37 | const StyledButtonText = styled.Text` 38 | color: ${colors.darkNeutral}; 39 | font-style: normal; 40 | font-weight: bold; 41 | line-height: 16px; 42 | font-size: 14px; 43 | letter-spacing: -0.0861539px; 44 | text-decoration: underline; 45 | text-decoration-color: ${colors.darkNeutral}; 46 | `; 47 | 48 | export const StyledLeftButton = styled.TouchableOpacity` 49 | flex-direction: row; 50 | align-items: center; 51 | justify-content: flex-start; 52 | margin-top: 10; 53 | `; 54 | 55 | const StyledRightButton = styled.TouchableOpacity` 56 | margin-right: 16; 57 | flex-direction: row; 58 | align-items: center; 59 | justify-content: center; 60 | `; 61 | 62 | export const StyledIcon = styled.Image``; 63 | 64 | export default (HeaderDetail = props => { 65 | const { 66 | leftHeaderIcon, 67 | leftHeaderButtonAction, 68 | leftHeaderButtonTitle, 69 | rightHeaderIcon, 70 | rightHeaderButtonAction, 71 | rightHeaderButtonTitle, 72 | headerTitle, 73 | hasRightSingleAvatar, 74 | user, 75 | hasRightGroupAvatar, 76 | group, 77 | } = props; 78 | const hasLeftIcon = leftHeaderIcon !== undefined && leftHeaderIcon !== ''; 79 | 80 | const hasRightIcon = rightHeaderIcon !== undefined && rightHeaderIcon !== ''; 81 | 82 | return ( 83 | <> 84 | 85 | 86 | {hasLeftIcon && } 87 | {leftHeaderButtonTitle !== '' && ( 88 | {leftHeaderButtonTitle} 89 | )} 90 | 91 | 92 | {headerTitle} 93 | 94 | {hasRightIcon && } 95 | {rightHeaderButtonTitle !== '' && ( 96 | {rightHeaderButtonTitle} 97 | )} 98 | {hasRightSingleAvatar && ( 99 | 103 | props.navigation.navigate('Profile', { 104 | user, 105 | }) 106 | } 107 | firstName={user.first_name} 108 | lastName={user.last_name} 109 | /> 110 | )} 111 | {hasRightGroupAvatar && ( 112 | 116 | console.log('on select user from paging view') 117 | } 118 | firstName={group.name} 119 | lastName="" 120 | /> 121 | )} 122 | 123 | 124 | 125 | 126 | 127 | ); 128 | }); 129 | -------------------------------------------------------------------------------- /src/components/Icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | 4 | const StyledIcon = styled.Image` 5 | width: ${props => props.size}; 6 | height: ${props => props.size}; 7 | `; 8 | 9 | export default (Icon = ({ source, size }) => ( 10 | 11 | )); 12 | -------------------------------------------------------------------------------- /src/components/ImagePicker.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import ImagePlaceholder from '../assets/images/ImagePlaceholder.png'; 4 | import GroupDefault from '../assets/images/GroupDefault.png'; 5 | import GrayAdd from '../assets/images/GrayAdd.png'; 6 | 7 | const StyledButton = styled.TouchableOpacity` 8 | width: 80; 9 | height: 80; 10 | border: 2px solid #e0e0e0; 11 | border-radius: 3px; 12 | margin: 10px; 13 | align-items: center; 14 | justify-content: center; 15 | `; 16 | 17 | const StyledImage = styled.Image` 18 | width: ${props => props.width}; 19 | height: ${props => props.height}; 20 | `; 21 | 22 | const StyledAdd = styled.Image` 23 | position: absolute; 24 | z-index: 100; 25 | bottom: -8; 26 | right: -8; 27 | height: 24; 28 | width: 24; 29 | `; 30 | 31 | export default (ImagePicker = props => ( 32 | 33 | 38 | 39 | 40 | )); 41 | -------------------------------------------------------------------------------- /src/components/ImagePreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ScrollView } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import RemovePhoto from '../assets/images/RemovePhoto.png'; 5 | import { DEVICE_WIDTH } from './HeaderDetail'; 6 | 7 | const StyledView = styled.View` 8 | flex-direction: row; 9 | margin: 10px 4px; 10 | `; 11 | 12 | const StyledImageView = styled.View``; 13 | 14 | const StyledImage = styled.Image` 15 | width: 80; 16 | height: 80; 17 | border-radius: 3; 18 | margin: 0px 6px; 19 | `; 20 | 21 | const StyledRemovePhoto = styled.Image` 22 | height: 25; 23 | width: 25; 24 | `; 25 | 26 | const StyledRemoveButton = styled.TouchableOpacity` 27 | position: absolute; 28 | z-index: 100; 29 | bottom: -8; 30 | right: -6; 31 | `; 32 | 33 | export default (ImagePreview = props => { 34 | if (props.images.length > 0) { 35 | return ( 36 | 37 | 38 | {props.images.length > 0 && 39 | props.images.map((value, index) => ( 40 | 41 | props.onPressRemove(value)}> 42 | 43 | 44 | 51 | 52 | ))} 53 | 54 | 55 | ); 56 | } 57 | return null; 58 | }); 59 | -------------------------------------------------------------------------------- /src/components/Label.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import colors from './Global/colors'; 4 | 5 | const StyledLabel = styled.Text` 6 | color: ${colors.black}; 7 | font-style: normal; 8 | font-weight: bold; 9 | line-height: 16px; 10 | font-size: 16px; 11 | letter-spacing: -0.0738462px; 12 | align-self: flex-start; 13 | margin-top: 19; 14 | margin-bottom: 17; 15 | `; 16 | 17 | export default (Label = ({ text }) => {text}); 18 | -------------------------------------------------------------------------------- /src/components/LargeButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | 5 | const DEVICE_WIDTH = Dimensions.get('window').width; 6 | 7 | const StyledLargeButton = styled.TouchableOpacity` 8 | width: ${DEVICE_WIDTH - 50}; 9 | height: 53; 10 | background-color: ${props => props.background}; 11 | justify-content: center; 12 | border-radius: 3; 13 | margin-top: 8; 14 | align-self: center; 15 | `; 16 | 17 | const StyledText = styled.Text` 18 | color: ${props => props.color || 'white'}; 19 | align-self: center; 20 | font-style: normal; 21 | font-weight: bold; 22 | line-height: 19px; 23 | font-size: 16px; 24 | text-align: center; 25 | letter-spacing: -0.0984616px; 26 | `; 27 | 28 | export default (LargeButton = props => { 29 | const { onPress, background, title, color } = props; 30 | return ( 31 | 32 | {title} 33 | 34 | ); 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/LayoutScrollViewWithHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions, Platform, RefreshControl } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import Header from './Header'; 5 | import colors from './Global/colors'; 6 | 7 | const DEVICE_WIDTH = Dimensions.get('window').width; 8 | const DEVICE_HEIGHT = Dimensions.get('window').height; 9 | 10 | const StyledSafeAreaView = styled.SafeAreaView` 11 | flex: 1; 12 | background-color: ${colors.primary}; 13 | `; 14 | 15 | const StyledScrollView = styled.ScrollView` 16 | padding-bottom: 30; 17 | `; 18 | 19 | const StyledOverlay = styled.View` 20 | height: ${DEVICE_HEIGHT}; 21 | width: ${DEVICE_WIDTH}; 22 | background-color: 'rgba(29, 30, 33,0.5)'; 23 | z-index: 1; 24 | position: absolute; 25 | `; 26 | 27 | /* 28 | View for Screen with scrolling content 29 | With sticky header outside scroll 30 | If there are textfields on the screen, use LayoutKeyboardAvoidingView 31 | */ 32 | 33 | const LayoutScrollView = ({ 34 | overlay, 35 | hasBar, 36 | width, 37 | backgroundColor, 38 | inputColor, 39 | value, 40 | placeholder, 41 | onChangeText, 42 | headerTitle, 43 | leftHeaderIcon, 44 | leftHeaderButtonAction, 45 | leftHeaderButtonTitle, 46 | rightHeaderButtonAction, 47 | rightHeaderButtonTitle, 48 | rightHeaderIcon, 49 | myRef, 50 | canRefresh, 51 | refreshing, 52 | onRefresh, 53 | children, 54 | }) => ( 55 | 56 | {overlay && } 57 |
73 | onRefresh()} 91 | /> 92 | ) 93 | } 94 | > 95 | {children} 96 | 97 | 98 | ); 99 | 100 | export default LayoutScrollView; 101 | -------------------------------------------------------------------------------- /src/components/LikeReactionItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import Ionicons from 'react-native-vector-icons/Ionicons'; 4 | import LikeIcon from '../assets/images/LikeIcon.png'; 5 | import LikeIconHighlighted from '../assets/images/LikeIconHighlighted.png'; 6 | import Icon from './Icon'; 7 | import colors from './Global/colors'; 8 | 9 | const StyledRowView = styled.View` 10 | flex-direction: row; 11 | align-items: center; 12 | justify-content: center; 13 | `; 14 | 15 | const StyledReactionImage = styled.TouchableOpacity``; 16 | 17 | const StyledReactionTitle = styled.Text` 18 | color: ${props => props.color}; 19 | text-align: right; 20 | font-weight: 600; 21 | line-height: 11px; 22 | font-size: 10px; 23 | letter-spacing: -0.0553846px; 24 | margin-left: 6; 25 | margin-right: 6; 26 | `; 27 | 28 | const StyledTouchableWithoutFeedback = styled.TouchableWithoutFeedback``; 29 | 30 | const StyledArrow = styled.View` 31 | width: 0; 32 | height: 0; 33 | border-top-color: ${props => props.color}; 34 | border-top-width: ${props => props.top}; 35 | border-right-color: transparent; 36 | border-bottom-width: ${props => props.bottom}; 37 | border-right-color: transparent; 38 | border-right-width: 6; 39 | border-left-color: transparent; 40 | border-left-width: 6; 41 | border-radius: 5; 42 | `; 43 | 44 | function determineString(count) { 45 | switch (count) { 46 | case 0: 47 | return 'Like'; 48 | case 1: 49 | return '1 like'; 50 | default: 51 | return `${count} likes`; 52 | } 53 | } 54 | 55 | export default (LikeReactionItem = ({ 56 | onPressReaction, 57 | userLiked, 58 | isComment, 59 | likeCount, 60 | toggleOpen, 61 | open, 62 | }) => ( 63 | 64 | 65 | 66 | 67 | 68 | 71 | {determineString(likeCount)} 72 | 73 | 74 | 79 | 80 | 81 | 82 | )); 83 | -------------------------------------------------------------------------------- /src/components/LoadMoreCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const StyledMainView = styled.View` 9 | width: ${DEVICE_WIDTH - 20}; 10 | margin-top: 10; 11 | margin-bottom: 30; 12 | align-items: center; 13 | justify-content: center; 14 | `; 15 | 16 | const StyledTextLinkButton = styled.TouchableOpacity``; 17 | 18 | const StyledTextLink = styled.Text` 19 | font-weight: 600; 20 | line-height: 26px; 21 | font-size: 16px; 22 | text-align: center; 23 | letter-spacing: -0.0861539px; 24 | color: ${colors.mediumNeutral}; 25 | text-decoration-line: underline; 26 | `; 27 | 28 | export default (LoadMoreCard = ({ onPress }) => ( 29 | 30 | 31 | Load more. 32 | 33 | 34 | )); 35 | -------------------------------------------------------------------------------- /src/components/LogoIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Image } from 'react-native'; 3 | import logo from '../assets/images/logo.png'; 4 | 5 | const LogoIcon = props => { 6 | return ; 7 | }; 8 | 9 | export default LogoIcon; 10 | -------------------------------------------------------------------------------- /src/components/NewComment.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import AutoGrowTextField from './AutoGrowTextField'; 5 | import colors from './Global/colors'; 6 | 7 | const DEVICE_WIDTH = Dimensions.get('window').width; 8 | 9 | const StyledRowView = styled.View` 10 | width: ${DEVICE_WIDTH - 48}; 11 | flex-direction: row; 12 | align-items: flex-start; 13 | margin-top: 2; 14 | margin-bottom: 24; 15 | `; 16 | 17 | const StyledView = styled.View``; 18 | 19 | export default (NewComment = ({ 20 | onChangeText, 21 | value, 22 | myRef, 23 | onFocus, 24 | onSubmit, 25 | }) => ( 26 | 27 | 28 | onChangeText(commentValue)} 31 | value={value} 32 | myRef={myRef} 33 | viewWidth={DEVICE_WIDTH - 48} 34 | width={DEVICE_WIDTH - 80} 35 | onFocus={onFocus} 36 | onSubmit={onSubmit} 37 | textColor={colors.mediumNeutral} 38 | shouldHaveSendButton 39 | /> 40 | 41 | 42 | )); 43 | -------------------------------------------------------------------------------- /src/components/NothingHereYetCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const StyledMainView = styled.View` 9 | width: ${DEVICE_WIDTH - 20}; 10 | margin-top: 200; 11 | margin-bottom: 30; 12 | align-items: center; 13 | justify-content: center; 14 | `; 15 | 16 | const StyledText = styled.Text` 17 | font-weight: 600; 18 | line-height: 26px; 19 | font-size: 16px; 20 | text-align: center; 21 | letter-spacing: -0.0861539px; 22 | color: ${colors.mediumNeutral}; 23 | margin-top: 10; 24 | `; 25 | 26 | const StyledTextLinkButton = styled.TouchableOpacity``; 27 | 28 | const StyledTextLink = styled.Text` 29 | font-weight: 600; 30 | line-height: 26px; 31 | font-size: 16px; 32 | text-align: center; 33 | letter-spacing: -0.0861539px; 34 | color: ${colors.mediumNeutral}; 35 | text-decoration-line: underline; 36 | `; 37 | 38 | export default (NothingHereyetCard = ({ onPress }) => ( 39 | 40 | No one has posted in this group yet! 41 | 42 | Get the conversation started. 43 | 44 | 45 | )); 46 | -------------------------------------------------------------------------------- /src/components/PagingView.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, Dimensions, SafeAreaView, Image } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import BackArrow from '../assets/images/BackArrow.png'; 5 | import colors from './Global/colors'; 6 | import Avatar from './Avatar'; 7 | 8 | const DEVICE_WIDTH = Dimensions.get('window').width; 9 | const IMAGE_WIDTH = Dimensions.get('window').width - 20; 10 | const IMAGE_HEIGHT = Dimensions.get('window').height / 2; 11 | 12 | const StyledHeader = styled.View` 13 | background-color: ${colors.black}; 14 | width: ${DEVICE_WIDTH}; 15 | flex-direction: column; 16 | align-items: flex-start; 17 | padding-left: 12; 18 | padding-top: 38; 19 | padding-bottom: 16; 20 | `; 21 | 22 | const StyledNameRowView = styled.View` 23 | flex-direction: row; 24 | align-items: center; 25 | `; 26 | 27 | const StyledName = styled.Text` 28 | margin-left: 8; 29 | line-height: 14px; 30 | font-size: 12px; 31 | letter-spacing: -0.0738462px; 32 | color: ${colors.white}; 33 | `; 34 | 35 | const StyledGroupName = styled.Text` 36 | font-weight: 600; 37 | line-height: 14px; 38 | font-size: 12px; 39 | letter-spacing: -0.0738462px; 40 | color: ${colors.mediumNeutral}; 41 | `; 42 | 43 | const StyledScrollView = styled.ScrollView` 44 | background-color: 'rgba(29, 30, 33, 0.85)'; 45 | `; 46 | 47 | const StyledMainImage = styled.Image` 48 | width: ${IMAGE_WIDTH}; 49 | height: ${IMAGE_HEIGHT}; 50 | align-self: center; 51 | margin-top: 10; 52 | `; 53 | 54 | const StyledCloseButton = styled.TouchableOpacity` 55 | margin-bottom: 16; 56 | `; 57 | 58 | export default (PagingView = ({ 59 | post, 60 | modalVisible, 61 | groupName, 62 | closeModal, 63 | }) => { 64 | const { images, user } = post; 65 | const { full_name } = user; 66 | return ( 67 | 68 | 69 | 70 | closeModal()}> 71 | 72 | 73 | 74 | 78 | console.log('on select user from paging view') 79 | } 80 | firstName={user.first_name} 81 | lastName={user.last_name} 82 | /> 83 | {`${full_name}, `} 84 | {groupName} 85 | 86 | 87 | 88 | {images.map((value, index) => ( 89 | 90 | ))} 91 | 92 | 93 | 94 | ); 95 | }); 96 | -------------------------------------------------------------------------------- /src/components/PickerItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import SInfo from 'react-native-sensitive-info'; 4 | import styled from 'styled-components/native'; 5 | import colors from './Global/colors'; 6 | 7 | const StyledOptionText = styled.Text` 8 | font-size: 18px; 9 | font-weight: 600; 10 | 11 | ${props => 12 | props.active && 13 | ` 14 | color: ${colors.highlightSoftRed}; 15 | `} 16 | `; 17 | 18 | const StyledOption = styled.TouchableOpacity` 19 | display: flex; 20 | justify-content: center; 21 | padding-left: 20px; 22 | height: 60; 23 | border-left-width: 4px; 24 | border-color: transparent; 25 | 26 | ${props => 27 | props.active && 28 | ` 29 | border-left-width: 4px; 30 | border-color: ${colors.highlightSoftRed}; 31 | `} 32 | `; 33 | 34 | const StyledIndicatorWrapper = styled.View` 35 | flex-direction: row; 36 | align-items: center; 37 | `; 38 | 39 | const StyledIndicator = styled.View` 40 | width: 10; 41 | height: 10; 42 | border-radius: 5; 43 | background-color: ${colors.mediumAccent}; 44 | margin-right: 5; 45 | `; 46 | 47 | class PickerItem extends Component { 48 | constructor(props) { 49 | super(props); 50 | 51 | this.state = { 52 | unread: false, 53 | }; 54 | } 55 | 56 | componentDidMount() { 57 | this.determineUnread(this.props.item); 58 | } 59 | 60 | determineItemTitle = (item, type) => { 61 | if (type === 'likes') { 62 | return item.user.full_name; 63 | } else if (type === 'messageRecipient') { 64 | return item.full_name; 65 | } else { 66 | return item.name; 67 | } 68 | }; 69 | 70 | determineUnread = item => { 71 | // console.log('ITEM ID ', item.id); 72 | SInfo.getItem(`${item.id}`, {}) 73 | .then(date => { 74 | // console.log('DATE ', date, item.name); 75 | if (date) { 76 | const unreadPosts = item.posts.filter( 77 | post => new Date(post.created_at) >= new Date(date), 78 | ); 79 | this.setState({ unread: unreadPosts.length > 0 }); 80 | } 81 | }) 82 | .catch(err => console.log('Error getting date ', err)); 83 | }; 84 | 85 | render() { 86 | const { item, activeId, selectedId, selectOption, type } = this.props; 87 | return ( 88 | { 90 | selectOption(item); 91 | }} 92 | active={(activeId && activeId === item.id) || selectedId === item.id} 93 | > 94 | 95 | {this.state.unread && } 96 | 101 | {this.determineItemTitle(item, type)} 102 | 103 | 104 | 105 | ); 106 | } 107 | } 108 | export default PickerItem; 109 | -------------------------------------------------------------------------------- /src/components/ProfileItemCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import { TextInputMask } from 'react-native-masked-text'; 4 | import styled from 'styled-components/native'; 5 | import colors from './Global/colors'; 6 | 7 | const DEVICE_WIDTH = Dimensions.get('window').width; 8 | 9 | const StyledView = styled.View` 10 | flex-direction: row; 11 | align-items: center; 12 | justify-content: space-between; 13 | width: ${DEVICE_WIDTH - 10}; 14 | background-color: ${colors.white}; 15 | margin-left: 10; 16 | padding-left: 10; 17 | border-radius: 3; 18 | margin-top: 10; 19 | align-self: flex-end; 20 | height: 53; 21 | `; 22 | 23 | const StyledIconView = styled.TouchableOpacity` 24 | margin-right: 24; 25 | `; 26 | 27 | const StyledImage = styled.Image``; 28 | 29 | const StyledInput = styled.TextInput` 30 | color: ${colors.darkNeutral}; 31 | font-style: normal; 32 | font-weight: 600; 33 | line-height: 19px; 34 | font-size: 14px; 35 | letter-spacing: -0.0984616px; 36 | flex: 1; 37 | `; 38 | 39 | const StyledPickerButton = styled.TouchableOpacity` 40 | width: ${DEVICE_WIDTH - 70}; 41 | `; 42 | 43 | const StyledLabel = styled.Text` 44 | color: ${colors.darkNeutral}; 45 | font-style: normal; 46 | font-weight: 600; 47 | line-height: 19px; 48 | font-size: 14px; 49 | letter-spacing: -0.0984616px; 50 | `; 51 | 52 | const StyledPlaceholder = styled.Text` 53 | color: ${colors.mediumNeutral}; 54 | font-style: normal; 55 | font-weight: 600; 56 | line-height: 19px; 57 | font-size: 14px; 58 | letter-spacing: -0.0984616px; 59 | `; 60 | 61 | const inputStyles = { 62 | color: colors.darkNeutral, 63 | fontStyle: 'normal', 64 | fontWeight: '600', 65 | lineHeight: 19, 66 | fontSize: 14, 67 | letterSpacing: -0.0984616, 68 | flex: 1, 69 | }; 70 | 71 | class ProfileItemCard extends Component { 72 | onPressIcon = () => { 73 | if (this.props.isMasked) { 74 | const el = this.refs.textfield.getElement(); 75 | // el.focus() 76 | el.focus(); 77 | } else if (this.props.isPicker) { 78 | this.props.onSelectPicker(); 79 | } else { 80 | this.textField.focus(); 81 | } 82 | }; 83 | 84 | handleEditIcon = () => { 85 | this.setState({ isEditable: true }); 86 | }; 87 | render() { 88 | let maskedValue; 89 | maskedValue = this.props.value === 'Invalid date' ? '' : this.props.value; 90 | maskedValue = this.props.value === 'null' ? '' : this.props.value; 91 | return ( 92 | 93 | {this.props.isMasked && ( 94 | this.props.onChangeText(value)} 101 | hasError={this.props.hasError} 102 | placeholderTextColor={colors.mediumNeutral} 103 | style={inputStyles} 104 | ref="textfield" 105 | /> 106 | )} 107 | 108 | {!this.props.isMasked && !this.props.isPicker && ( 109 | this.props.onChangeText(value)} 113 | hasError={this.props.hasError} 114 | placeholderTextColor={colors.mediumNeutral} 115 | ref={ref => (this.textField = ref)} 116 | /> 117 | )} 118 | {this.props.isPicker && ( 119 | 120 | {this.props.value && ( 121 | 122 | {this.props.value === 'null' ? null : this.props.value} 123 | 124 | )} 125 | {(!this.props.value || this.props.value === 'null') && ( 126 | {this.props.placeholder} 127 | )} 128 | 129 | )} 130 | this.onPressIcon()}> 131 | 132 | 133 | 134 | ); 135 | } 136 | } 137 | 138 | export default ProfileItemCard; 139 | -------------------------------------------------------------------------------- /src/components/ReactionTabBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | import LikeReactionItem from './LikeReactionItem'; 6 | import CommentReactionItem from './CommentReactionItem'; 7 | 8 | const DEVICE_WIDTH = Dimensions.get('window').width; 9 | 10 | const StyledReactionTab = styled.View` 11 | width: ${DEVICE_WIDTH - 20}; 12 | align-self: center; 13 | flex-direction: row; 14 | align-items: center; 15 | justify-content: center; 16 | border-top-width: 2; 17 | border-color: ${colors.background}; 18 | background-color: ${colors.white}; 19 | `; 20 | 21 | const StyledRowView = styled.View` 22 | flex-direction: row; 23 | align-items: center; 24 | justify-content: space-between; 25 | width: ${DEVICE_WIDTH - 48}; 26 | margin-top: 6; 27 | margin-bottom: 6; 28 | `; 29 | 30 | export default (ReactionTabBar = ({ 31 | likeCount, 32 | onPressLike, 33 | likesOpen, 34 | toggleLeftReaction, 35 | userLiked, 36 | commentCount, 37 | onPressComment, 38 | commentsOpen, 39 | toggleRightReaction, 40 | userCommented, 41 | }) => ( 42 | 43 | 44 | 52 | 60 | 61 | 62 | )); 63 | -------------------------------------------------------------------------------- /src/components/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Ionicons from 'react-native-vector-icons/Ionicons'; 3 | import styled from 'styled-components/native'; 4 | 5 | const StyledView = styled.View` 6 | flex-direction: row; 7 | align-items: center; 8 | justify-content: space-between; 9 | background-color: ${props => props.backgroundColor}; 10 | padding: 0 12px; 11 | color: black; 12 | padding-left: 8; 13 | border-radius: 8; 14 | width: ${props => props.width}; 15 | `; 16 | 17 | const StyledInput = styled.TextInput` 18 | padding-left: 8; 19 | color: ${props => props.inputColor}; 20 | font-style: normal; 21 | font-weight: 600; 22 | font-size: 13px; 23 | letter-spacing: -0.0738462px; 24 | flex: 1; 25 | `; 26 | 27 | export default (SearchBar = ({ 28 | width, 29 | backgroundColor, 30 | inputColor, 31 | value, 32 | placeholder, 33 | onChangeText, 34 | placeholderTextColor, 35 | }) => ( 36 | 37 | 44 | 45 | 46 | )); 47 | -------------------------------------------------------------------------------- /src/components/SectionCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | export const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const Card = styled.View` 9 | width: ${DEVICE_WIDTH - 10}; 10 | background-color: ${colors.white}; 11 | padding-left: 10; 12 | border-radius: 3; 13 | margin-top: 10; 14 | align-self: flex-end; 15 | `; 16 | 17 | const CardTitle = styled.Text` 18 | color: ${colors.black}; 19 | font-style: normal; 20 | font-weight: bold; 21 | line-height: 16px; 22 | font-size: 16px; 23 | letter-spacing: -0.0738462px; 24 | margin-bottom: 12; 25 | margin-top: 16; 26 | `; 27 | 28 | const SectionCard = ({ title, children, ...props }) => ( 29 | 30 | {title && {title}} 31 | {children} 32 | 33 | ); 34 | 35 | export default SectionCard; 36 | -------------------------------------------------------------------------------- /src/components/SectionCardCentered.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dimensions } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const Card = styled.View` 9 | width: ${DEVICE_WIDTH - 20}; 10 | background-color: ${colors.white}; 11 | padding-left: 10; 12 | border-radius: 3; 13 | border-color: ${props => (props.hasError ? colors.error : colors.white)}; 14 | border-width: 2; 15 | margin-top: 10; 16 | align-self: center; 17 | `; 18 | 19 | const CardTitle = styled.Text` 20 | color: ${colors.black}; 21 | font-style: normal; 22 | font-weight: bold; 23 | line-height: 16px; 24 | font-size: 16px; 25 | letter-spacing: -0.0738462px; 26 | margin-bottom: 12; 27 | margin-top: 16; 28 | `; 29 | 30 | const SectionCardCentered = ({ title, children, ...props }) => ( 31 | 32 | {title && {title}} 33 | {children} 34 | 35 | ); 36 | 37 | export default SectionCardCentered; 38 | -------------------------------------------------------------------------------- /src/components/SwitchCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Dimensions, Switch } from 'react-native'; 3 | import styled from 'styled-components/native'; 4 | import colors from './Global/colors'; 5 | 6 | const DEVICE_WIDTH = Dimensions.get('window').width; 7 | 8 | const StyledView = styled.View``; 9 | 10 | const StyledListItem = styled.View` 11 | flex-direction: row; 12 | align-items: center; 13 | justify-content: space-between; 14 | height: 85; 15 | `; 16 | 17 | const StyledListText = styled.Text` 18 | font-style: normal; 19 | font-weight: 600; 20 | line-height: 18px; 21 | font-size: 14px; 22 | letter-spacing: -0.0861539px; 23 | color: ${colors.black}; 24 | width: 230; 25 | `; 26 | 27 | const StyledListSwitch = styled.View` 28 | margin-right: 20; 29 | `; 30 | 31 | const StyledSeparator = styled.View` 32 | height: 2; 33 | background-color: ${colors.background}; 34 | width: ${DEVICE_WIDTH - 20}; 35 | align-self: flex-end; 36 | `; 37 | 38 | class SwitchCard extends Component { 39 | constructor(props) { 40 | super(props); 41 | 42 | this.state = { 43 | isOn: false, 44 | }; 45 | } 46 | 47 | componentDidMount() { 48 | this.determineIfOn(); 49 | } 50 | 51 | determineIfOn = () => { 52 | const filtered = this.props.currentSettings.filter( 53 | el => el === this.props.value.key, 54 | ); 55 | if (filtered.length > 0) { 56 | this.setState({ isOn: true }); 57 | } else { 58 | this.setState({ isOn: false }); 59 | } 60 | }; 61 | 62 | handleSwitchChange = () => { 63 | this.props.onPress(); 64 | this.setState({ isOn: !this.state.isOn }); 65 | // Make call to toggle notification setting based on id/type/etc. 66 | }; 67 | 68 | render() { 69 | return ( 70 | 71 | 72 | {this.props.value.value} 73 | 74 | this.handleSwitchChange()} 83 | /> 84 | 85 | 86 | 87 | 88 | ); 89 | } 90 | } 91 | 92 | export default SwitchCard; 93 | -------------------------------------------------------------------------------- /src/components/TextButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | 4 | const Button = styled.TouchableOpacity` 5 | padding: 10px 0; 6 | display: flex; 7 | `; 8 | 9 | const Text = styled.Text` 10 | font-style: normal; 11 | font-weight: 600; 12 | line-height: 14px; 13 | font-size: 12px; 14 | letter-spacing: -0.0738462px; 15 | color: #d2fbf8; 16 | text-decoration: underline; 17 | text-decoration-color: #d2fbf8; 18 | `; 19 | 20 | const TextButton = ({ 21 | children, 22 | buttonStyle, 23 | textColor, 24 | textStyle, 25 | onPress, 26 | ...props 27 | }) => ( 28 | 33 | ); 34 | 35 | export default TextButton; 36 | -------------------------------------------------------------------------------- /src/components/TextField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import colors from './Global/colors'; 4 | import { DEVICE_WIDTH } from './Header'; 5 | 6 | const StyledInput = styled.TextInput` 7 | flex: 1; 8 | height: 53px; 9 | background-color: transparent; 10 | border-color: ${props => props.border}; 11 | border-width: 1; 12 | color: ${colors.darkNeutral}; 13 | padding-top: 10; 14 | padding-bottom: 6; 15 | padding-left: 8; 16 | margin-top: 8; 17 | border: 0; 18 | border-bottom-width: 1px; 19 | border-bottom-color: ${colors.darkerBase}; 20 | font-style: normal; 21 | font-weight: 600; 22 | line-height: 19px; 23 | font-size: 14px; 24 | letter-spacing: -0.0984616px; 25 | width: ${DEVICE_WIDTH - 50}; 26 | `; 27 | 28 | const StyledError = styled.Text` 29 | width: 300; 30 | margin-bottom: 2; 31 | color: red; 32 | padding-left: 6; 33 | padding-top: 4; 34 | padding-bottom: 4; 35 | align-self: flex-start; 36 | `; 37 | 38 | export default (TextField = props => ( 39 | 53 | )); 54 | -------------------------------------------------------------------------------- /src/components/Timestamp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import moment from 'moment'; 3 | import styled from 'styled-components/native'; 4 | 5 | // checks for current date, subtracts 3 days 6 | // if timestamp is before 3 days, display date 7 | // if within last 3 days, use timeago to show time 8 | export default function createTimeStamp(date) { 9 | const StyledTimeStamp = styled.Text` 10 | text-align: right; 11 | color: gray; 12 | font-size: 13; 13 | padding-right: 8; 14 | `; 15 | return {moment(date).fromNow()}; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/TransparentInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TextField from './TextField'; 3 | import colors from './Global/colors'; 4 | import styled from 'styled-components/native'; 5 | import EditIconPencil from '../assets/images/EditIconPencil.png'; 6 | 7 | const StyledView = styled.View` 8 | justify-content: space-between; 9 | align-items: center; 10 | flex-direction: row; 11 | `; 12 | 13 | const StyledEditIcon = styled.Image` 14 | margin-right: 28; 15 | `; 16 | 17 | const transparentInputStyles = { 18 | borderBottomWidth: 0, 19 | fontStyle: 'normal', 20 | color: colors.darkNeutral, 21 | fontWeight: '600', 22 | marginTop: 0, 23 | paddingTop: 10, 24 | paddingBottom: 10, 25 | paddingLeft: 5, 26 | }; 27 | 28 | const TransparentInput = props => ( 29 | 30 | 35 | 36 | 37 | ); 38 | 39 | export default TransparentInput; 40 | -------------------------------------------------------------------------------- /src/constants/urls.js: -------------------------------------------------------------------------------- 1 | export const BASE_URL = `https://cratebind.com/api/v1`; 2 | 3 | // CONVERSATIONS 4 | export const GET_ALL_CONVERSATIONS = ( 5 | organizationId, 6 | page, 7 | ) => `${BASE_URL}/conversations?organization_id=${organizationId}&page=${page || 8 | 1}&per=20 9 | `; 10 | export const CREATE_CONVERSATION = `${BASE_URL}/conversations`; 11 | export const GET_SINGLE_CONVERSATION = convoId => 12 | `${BASE_URL}/conversations/${convoId}`; 13 | export const MARK_CONVERSATION_READ = id => 14 | `${BASE_URL}/conversations/${id}/mark_read`; 15 | export const POST_MESSAGE_TO_CONVERSATION = convoId => 16 | `${BASE_URL}/conversations/${convoId}/messages`; 17 | export const DELETE_CONVERSATION = convoId => 18 | `${BASE_URL}/conversations/${convoId}`; 19 | 20 | // DEVICES 21 | export const REGISTER_DEVICE = `${BASE_URL}/devices`; 22 | export const EDIT_DEVICE = deviceId => `${BASE_URL}/devices/${deviceId}`; 23 | export const DELETE_DEVICE = deviceId => `${BASE_URL}/devices/${deviceId}`; 24 | 25 | // NOTIFICATIONS 26 | export const GET_ALL_NOTIFICATIONS = (userId, page) => 27 | `${BASE_URL}/users/${userId}/notifications?page=${page || 1}&per=20`; 28 | export const GET_AVAILABLE_NOTIFICATIONS = `${BASE_URL}/notifications/available_notifications`; 29 | export const MARK_NOTIFICATION_READ = notifId => 30 | `${BASE_URL}/notifications/${notifId}/mark_read`; 31 | 32 | // POSTS 33 | export const GET_ALL_POSTS_FROM_GROUP = (groupId, page) => 34 | `${BASE_URL}/groups/${groupId}/posts?page=${page || 1}&per=20`; 35 | export const GET_ALL_POSTS = (organizationId, page) => 36 | `${BASE_URL}/posts?organization_id=${organizationId}&page=${page || 37 | 1}&per=20`; 38 | export const CREATE_POST = `${BASE_URL}/posts`; 39 | export const UNLIKE_POST = postId => `${BASE_URL}/posts/${postId}/unlike`; 40 | export const LIKE_POST = postId => `${BASE_URL}/posts/${postId}/like`; 41 | export const DELETE_POST = postId => `${BASE_URL}/posts/${postId}`; 42 | export const GET_SINGLE_POST = postId => `${BASE_URL}/posts/${postId}`; 43 | export const EDIT_POST = postId => `${BASE_URL}/posts/${postId}`; 44 | // comments on posts 45 | export const CREATE_COMMENT_ON_POST = postId => 46 | `${BASE_URL}/posts/${postId}/comments`; 47 | export const UNLIKE_COMMENT_ON_POST = (postId, commentId) => 48 | `${BASE_URL}/posts/${postId}/comments/${commentId}/unlike`; 49 | export const LIKE_COMMENT_ON_POST = (postId, commentId) => 50 | `${BASE_URL}/posts/${postId}/comments/${commentId}/like`; 51 | export const DELETE_COMMENT_ON_POST = (postId, commentId) => 52 | `${BASE_URL}/posts/${postId}/comments/${commentId}`; 53 | export const EDIT_COMMENT_ON_POST = (postId, commentId) => 54 | `${BASE_URL}/posts/${postId}/comments/${commentId}`; 55 | 56 | // USERS 57 | export const SIGN_UP = `${BASE_URL}/users/sign_up`; 58 | export const SIGN_IN = `${BASE_URL}/users/sign_in`; 59 | export const SIGN_OUT = `${BASE_URL}/users/sign_out`; 60 | export const FORGOT_PASSWORD = `${BASE_URL}/users/forgot_password`; 61 | export const UPDATE_PROFILE = userId => `${BASE_URL}/users/${userId}`; 62 | export const GET_CURRENT_USER = `${BASE_URL}/users`; 63 | export const GET_SINGLE_USER = userId => `${BASE_URL}/users/${userId}`; 64 | export const GET_NOTIFICATIONS_FOR_USER = (userId, page) => 65 | `${BASE_URL}/users/${userId}/notifications?page=${page || 1}&per=20`; 66 | export const TOGGLE_NOTIFICATION = userId => 67 | `${BASE_URL}/users/${userId}/toggle_notifications`; 68 | -------------------------------------------------------------------------------- /src/navigation/AuthStack.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation'; 2 | import SignInScreen from '../screens/SignInScreen'; 3 | import SignUpScreen from '../screens/SignUpScreen'; 4 | import LaunchScreen from '../screens/LaunchScreen'; 5 | import ForgotPasswordScreen from '../screens/ForgotPasswordScreen'; 6 | 7 | const AuthStack = createStackNavigator( 8 | { 9 | Launch: LaunchScreen, 10 | SignIn: SignInScreen, 11 | SignUp: SignUpScreen, 12 | ForgotPassword: ForgotPasswordScreen, 13 | }, 14 | { 15 | headerMode: 'none', 16 | gesturesEnabled: false, 17 | }, 18 | ); 19 | 20 | export default AuthStack; 21 | -------------------------------------------------------------------------------- /src/navigation/MainFeedStack.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation'; 2 | import MainFeedScreen from '../screens/MainFeedScreen'; 3 | import CreatePostScreen from '../screens/CreatePostScreen'; 4 | import PostShow from '../screens/PostShowScreen'; 5 | 6 | const MainFeedStack = createStackNavigator( 7 | { 8 | MainFeed: MainFeedScreen, 9 | CreatePost: CreatePostScreen, 10 | PostDetail: PostShow, 11 | }, 12 | { 13 | headerMode: 'none', 14 | gesturesEnabled: false, 15 | }, 16 | ); 17 | 18 | export default MainFeedStack; 19 | -------------------------------------------------------------------------------- /src/navigation/MainSwitchNavigator.js: -------------------------------------------------------------------------------- 1 | import { createSwitchNavigator, createAppContainer } from 'react-navigation'; 2 | 3 | import AuthStack from './AuthStack'; 4 | import TabNavigator from './TabNavigator'; 5 | 6 | const MainSwitchNavigator = createSwitchNavigator({ 7 | SignedOut: AuthStack, 8 | SignedIn: TabNavigator, 9 | }); 10 | 11 | export default createAppContainer(MainSwitchNavigator); 12 | -------------------------------------------------------------------------------- /src/navigation/MessagesStack.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation'; 2 | import MessagesScreen from '../screens/MessagesScreen'; 3 | import MessagesShowScreen from '../screens/MessageShowScreen'; 4 | import CreateMessageScreen from '../screens/CreateMessageScreen'; 5 | 6 | const MainFeedStack = createStackNavigator( 7 | { 8 | Messages: MessagesScreen, 9 | MessageDetail: MessagesShowScreen, 10 | CreateMessage: CreateMessageScreen, 11 | }, 12 | { 13 | headerMode: 'none', 14 | gesturesEnabled: false, 15 | }, 16 | ); 17 | 18 | export default MainFeedStack; 19 | -------------------------------------------------------------------------------- /src/navigation/SettingsStack.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation'; 2 | import SettingsScreen from '../screens/SettingsScreen'; 3 | import ProfileScreen from '../screens/ProfileScreen'; 4 | import EditProfileScreen from '../screens/EditProfileScreen'; 5 | 6 | const MoreStack = createStackNavigator( 7 | { 8 | Profile: ProfileScreen, 9 | EditProfile: EditProfileScreen, 10 | Settings: SettingsScreen, 11 | }, 12 | { 13 | headerMode: 'none', 14 | gesturesEnabled: false, 15 | }, 16 | ); 17 | 18 | export default MoreStack; 19 | -------------------------------------------------------------------------------- /src/navigation/TabNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Platform, View } from 'react-native'; 3 | import { createBottomTabNavigator, TabBarBottom } from 'react-navigation'; 4 | 5 | import styled from 'styled-components/native'; 6 | import HomeIcon from '../assets/images/HomeIcon.png'; 7 | import HomeIconFilled from '../assets/images/HomeIconFilled.png'; 8 | // import MessageIcon from '../assets/images/MessageIcon.png'; 9 | // import MessageIconFilled from '../assets/images/MessageIconFilled.png'; 10 | // import NotificationIcon from '../assets/images/NotificationIcon.png'; 11 | // import NotificationIconFilled from '../assets/images/NotificationIconFilled.png'; 12 | import MoreIcon from '../assets/images/MoreIcon.png'; 13 | import MoreIconFilled from '../assets/images/MoreIconFilled.png'; 14 | 15 | import MainFeedStack from './MainFeedStack'; 16 | 17 | import SettingsStack from './SettingsStack'; 18 | 19 | import colors from '../components/Global/colors'; 20 | 21 | // import NotificationIndicator from '../PushNotifications/NotificationIndicator'; 22 | // import MessageIndicator from '../PushNotifications/MessageIndicator'; 23 | 24 | const StyledIcon = styled.Image` 25 | height: ${props => props.height}; 26 | width: ${props => props.width}; 27 | `; 28 | 29 | const TabNavigator = createBottomTabNavigator( 30 | { 31 | MainFeedTab: { 32 | screen: MainFeedStack, 33 | navigationOptions: { 34 | tabBarIcon: ({ tintColor, focused }) => ( 35 | 40 | ), 41 | }, 42 | }, 43 | // MessagesTab: { 44 | // screen: MessagesStack, 45 | // navigationOptions: { 46 | // tabBarIcon: ({ tintColor, focused }) => ( 47 | // 48 | // 49 | // 54 | // 55 | // ), 56 | // }, 57 | // }, 58 | // NotificationsTab: { 59 | // screen: NotificationsScreen, 60 | // navigationOptions: { 61 | // tabBarIcon: ({ tintColor, focused }) => ( 62 | // 63 | // 64 | // 69 | // 70 | // ), 71 | // }, 72 | // }, 73 | MoreTab: { 74 | screen: SettingsStack, 75 | navigationOptions: { 76 | tabBarIcon: ({ tintColor, focused }) => ( 77 | 82 | ), 83 | }, 84 | }, 85 | }, 86 | { 87 | tabBarOptions: { 88 | showLabel: false, 89 | activeTintColor: colors.primary, 90 | inactiveTintColor: 'gray', 91 | }, 92 | tabBarComponent: Platform.OS === 'ios' ? TabBarBottom : TabBarAndroid, 93 | tabBarPosition: 'bottom', 94 | }, 95 | ); 96 | 97 | export default TabNavigator; 98 | -------------------------------------------------------------------------------- /src/redux/actions/categories.actions.js: -------------------------------------------------------------------------------- 1 | import { getAll } from '../api/categories'; 2 | 3 | // Action constants 4 | export const CATEGORIES = 'CATEGORIES'; 5 | 6 | // actions 7 | export function getAllCategories() { 8 | return async dispatch => { 9 | try { 10 | const response = await getAll(); 11 | 12 | // console.log('GET ALL CATEGORIES', response); 13 | 14 | if (response.categories) { 15 | dispatch({ 16 | type: CATEGORIES, 17 | payload: { 18 | status: `complete ${new Date()}`, 19 | results: [...response.categories], 20 | }, 21 | }); 22 | } else if (response.error) { 23 | dispatch({ 24 | type: CATEGORIES, 25 | payload: { status: 'failed', results: [] }, 26 | }); 27 | } 28 | } catch (error) { 29 | // console.log('Error getting categories', error); 30 | } 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/redux/actions/loginType.actions.js: -------------------------------------------------------------------------------- 1 | export const SET_LOGIN_TYPE = 'SET_LOGIN_TYPE'; 2 | 3 | // Need to set a login type when login action is sent 4 | // This prevents navigation from navigating to signed in several times 5 | 6 | export function setLoginType(type) { 7 | return dispatch => { 8 | dispatch({ 9 | type: SET_LOGIN_TYPE, 10 | payload: type, 11 | }); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/redux/actions/messageIndicator.actions.js: -------------------------------------------------------------------------------- 1 | export const MESSAGE_INDICATOR = 'MESSAGE_INDICATOR'; 2 | 3 | // Need to set a login type when login action is sent 4 | // This prevents navigation from navigating to signed in several times 5 | 6 | export function setMessageIndicator(shouldShow) { 7 | return dispatch => { 8 | dispatch({ 9 | type: MESSAGE_INDICATOR, 10 | payload: { shouldShow, updatedAt: new Date() }, 11 | }); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/redux/actions/notificationIndicator.actions.js: -------------------------------------------------------------------------------- 1 | export const NOTIFICATION_INDICATOR = 'NOTIFICATION_INDICATOR'; 2 | 3 | // Need to set a login type when login action is sent 4 | // This prevents navigation from navigating to signed in several times 5 | 6 | export function setNotificationIndicator(shouldShow) { 7 | return dispatch => { 8 | dispatch({ 9 | type: NOTIFICATION_INDICATOR, 10 | payload: { shouldShow, updatedAt: new Date() }, 11 | }); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/redux/actions/organizations.actions.js: -------------------------------------------------------------------------------- 1 | import { getAll, getSingle } from '../api/organizations'; 2 | 3 | // Action constants 4 | export const ORGANIZATIONS = 'ORGANIZATIONS'; 5 | 6 | // actions 7 | export function getAllOrganizations() { 8 | return async dispatch => { 9 | try { 10 | const response = await getAll(); 11 | 12 | // console.log('GET ALL ORGANIZATIONS', response); 13 | 14 | // 15 | 16 | if (response.organizations) { 17 | dispatch({ 18 | type: ORGANIZATIONS, 19 | payload: { 20 | status: `allComplete${new Date()}`, 21 | results: [...response.organizations], 22 | }, 23 | }); 24 | } else if (response.error) { 25 | dispatch({ 26 | type: ORGANIZATIONS, 27 | payload: { status: 'failed', results: [] }, 28 | }); 29 | } 30 | } catch (error) { 31 | // console.log('Error getting organizations', error); 32 | } 33 | }; 34 | } 35 | 36 | export function getSingleOrganization(orgId) { 37 | return async dispatch => { 38 | try { 39 | const response = await getAll(); 40 | 41 | // console.log('GET ALL RESPONSE ', response); 42 | if (response.organizations) { 43 | const filteredCurrentUserOrg = response.organizations.filter( 44 | org => org.id === orgId, 45 | )[0]; 46 | // console.log('FILTERED ', filteredCurrentUserOrg); 47 | const singleResponse = await getSingle(orgId); 48 | 49 | if (singleResponse.public_organization) { 50 | console.log('SINGLE ORGANIZATION ', { 51 | ...filteredCurrentUserOrg, 52 | users: singleResponse.public_organization.users, 53 | }); 54 | 55 | dispatch({ 56 | type: ORGANIZATIONS, 57 | payload: { 58 | status: `singleComplete${new Date()}`, 59 | results: { 60 | ...filteredCurrentUserOrg, 61 | users: singleResponse.public_organization.users, 62 | }, 63 | }, 64 | }); 65 | } 66 | } else if (response.error) { 67 | dispatch({ 68 | type: ORGANIZATIONS, 69 | payload: { status: 'failed', results: [] }, 70 | }); 71 | } 72 | } catch (error) { 73 | // console.log('Error getting single organization', error); 74 | } 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /src/redux/actions/resources.actions.js: -------------------------------------------------------------------------------- 1 | import { getAll } from '../api/resources'; 2 | 3 | // Action constants 4 | export const RESOURCES = 'RESOURCES'; 5 | 6 | // actions 7 | export function getAllResources() { 8 | return async dispatch => { 9 | try { 10 | const response = await getAll(); 11 | 12 | // console.log('GET ALL RESOURCES', response); 13 | 14 | // 15 | 16 | if (response.resources) { 17 | dispatch({ 18 | type: RESOURCES, 19 | payload: { 20 | status: `allResources ${new Date()}`, 21 | results: response.resources, 22 | }, 23 | }); 24 | } else if (response.error) { 25 | dispatch({ 26 | type: RESOURCES, 27 | payload: { status: 'failed', results: [] }, 28 | }); 29 | } 30 | } catch (error) { 31 | // console.log('Error getting resources', error); 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/redux/actions/tabBar.actions.js: -------------------------------------------------------------------------------- 1 | export const SELECTED_TAB = 'SELECTED_TAB'; 2 | 3 | export function setSelectedTab(tab) { 4 | return dispatch => { 5 | dispatch({ 6 | type: SELECTED_TAB, 7 | payload: { selectedTab: tab, updatedAt: new Date() }, 8 | }); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/redux/api/categories.js: -------------------------------------------------------------------------------- 1 | import { GET_ALL_CATEGORIES } from '../../constants/urls'; 2 | import SInfo from 'react-native-sensitive-info'; 3 | 4 | export const getAll = async () => { 5 | const orgId = await SInfo.getItem('defaultOrgId', {}); 6 | const token = await SInfo.getItem('authToken', {}); 7 | // console.log('GET ALL CATEGORIES URL ', GET_ALL_CATEGORIES(parseInt(orgId))); 8 | const response = await fetch(GET_ALL_CATEGORIES(parseInt(orgId)), { 9 | headers: { 10 | 'Content-Type': 'application/json', 11 | Authorization: token, 12 | }, 13 | }); 14 | return response.json(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/redux/api/conversations.js: -------------------------------------------------------------------------------- 1 | // Add auth token to all authorization headers 2 | import { 3 | GET_ALL_CONVERSATIONS, 4 | CREATE_CONVERSATION, 5 | GET_SINGLE_CONVERSATION, 6 | MARK_CONVERSATION_READ, 7 | POST_MESSAGE_TO_CONVERSATION, 8 | DELETE_CONVERSATION, 9 | } from '../../constants/urls'; 10 | import SInfo from 'react-native-sensitive-info'; 11 | 12 | export const get = async page => { 13 | const orgId = await SInfo.getItem('defaultOrgId', {}); 14 | const token = await SInfo.getItem('authToken', {}); 15 | const response = await fetch(GET_ALL_CONVERSATIONS(orgId, page), { 16 | headers: { 17 | 'Content-Type': 'application/json', 18 | Authorization: token, 19 | }, 20 | }); 21 | return response.json(); 22 | }; 23 | 24 | export const create = async formdata => { 25 | const orgId = await SInfo.getItem('defaultOrgId', {}); 26 | const token = await SInfo.getItem('authToken', {}); 27 | formdata.append('organization_id', parseInt(orgId)); 28 | // console.log(formdata); 29 | 30 | const response = await fetch(CREATE_CONVERSATION, { 31 | method: 'POST', 32 | headers: { 33 | 'Content-Type': 'application/json', 34 | Authorization: token, 35 | }, 36 | body: formdata, 37 | }); 38 | // console.log(response); 39 | return response.json(); 40 | }; 41 | 42 | export const getSingle = async convoId => { 43 | const token = await SInfo.getItem('authToken', {}); 44 | const response = await fetch(GET_SINGLE_CONVERSATION(convoId), { 45 | headers: { 46 | 'Content-Type': 'application/json', 47 | Authorization: token, 48 | }, 49 | }); 50 | return response.json(); 51 | }; 52 | 53 | export const mark = async convoId => { 54 | const token = await SInfo.getItem('authToken', {}); 55 | const response = await fetch(MARK_CONVERSATION_READ(convoId), { 56 | method: 'PUT', 57 | headers: { 58 | 'Content-Type': 'application/json', 59 | Authorization: token, 60 | }, 61 | }); 62 | // console.log(response); 63 | return response.json(); 64 | }; 65 | 66 | export const post = async (convoId, content) => { 67 | const token = await SInfo.getItem('authToken', {}); 68 | const response = await fetch(POST_MESSAGE_TO_CONVERSATION(convoId), { 69 | method: 'POST', 70 | headers: { 71 | 'Content-Type': 'application/json', 72 | Authorization: token, 73 | }, 74 | body: JSON.stringify({ 75 | content, 76 | }), 77 | }); 78 | // console.log(response); 79 | return response.json(); 80 | }; 81 | 82 | export const deleteConv = async convoId => { 83 | const token = await SInfo.getItem('authToken', {}); 84 | const response = await fetch(DELETE_CONVERSATION(convoId), { 85 | method: 'DELETE', 86 | headers: { 87 | 'Content-Type': 'application/json', 88 | Authorization: token, 89 | }, 90 | }); 91 | return response.json(); 92 | }; 93 | -------------------------------------------------------------------------------- /src/redux/api/groups.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_ALL_GROUPS, 3 | CREATE_GROUP, 4 | LEAVE_GROUP, 5 | ADD_TO_GROUP, 6 | REQUEST_TO_JOIN_GROUP, 7 | GET_SINGLE_GROUP, 8 | EDIT_GROUP, 9 | ASSIGN_GROUP_ADMIN, 10 | ACCEPT_REQUEST, 11 | REMOVE_USER, 12 | REQUEST_TO_ADD_USER_TO_GROUP, 13 | } from '../../constants/urls'; 14 | import SInfo from 'react-native-sensitive-info'; 15 | 16 | export const getAll = async page => { 17 | const orgId = await SInfo.getItem('defaultOrgId', {}); 18 | const token = await SInfo.getItem('authToken', {}); 19 | const response = await fetch(GET_ALL_GROUPS(parseInt(orgId), page), { 20 | headers: { 21 | 'Content-Type': 'application/json', 22 | Authorization: token, 23 | }, 24 | }); 25 | return response.json(); 26 | }; 27 | 28 | export const getSingle = async groupId => { 29 | const token = await SInfo.getItem('authToken', {}); 30 | const response = await fetch(GET_SINGLE_GROUP(groupId), { 31 | headers: { 32 | 'Content-Type': 'application/json', 33 | Authorization: token, 34 | }, 35 | }); 36 | return response.json(); 37 | }; 38 | 39 | export const create = async (formdata, users) => { 40 | const orgId = await SInfo.getItem('defaultOrgId', {}); 41 | const token = await SInfo.getItem('authToken', {}); 42 | formdata.append('organization_id', parseInt(orgId)); 43 | // console.log('CREATE GROUP WITH USERS URL ', CREATE_GROUP(users)); 44 | const response = await fetch(CREATE_GROUP(users), { 45 | method: 'POST', 46 | headers: { 47 | Authorization: token, 48 | }, 49 | body: formdata, 50 | }); 51 | // console.log('form data ', formdata); 52 | console.log('response ', response); 53 | return response.json(); 54 | }; 55 | 56 | export const leave = async groupId => { 57 | const token = await SInfo.getItem('authToken', {}); 58 | const response = await fetch(LEAVE_GROUP(groupId), { 59 | method: 'PUT', 60 | headers: { 61 | 'Content-Type': 'application/json', 62 | Authorization: token, 63 | }, 64 | }); 65 | return response.json(); 66 | }; 67 | 68 | export const add = async (groupId, userId, isAdmin) => { 69 | // console.log(groupId, userId, isAdmin); 70 | // console.log(ADD_TO_GROUP(groupId)); 71 | const token = await SInfo.getItem('authToken', {}); 72 | const response = await fetch(ADD_TO_GROUP(groupId), { 73 | method: 'PUT', 74 | headers: { 75 | 'Content-Type': 'application/json', 76 | Authorization: token, 77 | }, 78 | body: JSON.stringify({ 79 | user_id: userId, 80 | is_admin: isAdmin, 81 | }), 82 | }); 83 | // console.log(response); 84 | return response.json(); 85 | }; 86 | 87 | export const request = async groupId => { 88 | const token = await SInfo.getItem('authToken', {}); 89 | const response = await fetch(REQUEST_TO_JOIN_GROUP(groupId), { 90 | headers: { 91 | 'Content-Type': 'application/json', 92 | Authorization: token, 93 | }, 94 | }); 95 | // console.log(response); 96 | return response.json(); 97 | }; 98 | 99 | export const requestOtherUser = async (groupId, userId) => { 100 | const token = await SInfo.getItem('authToken', {}); 101 | const response = await fetch(REQUEST_TO_ADD_USER_TO_GROUP(groupId, userId), { 102 | headers: { 103 | 'Content-Type': 'application/json', 104 | Authorization: token, 105 | }, 106 | }); 107 | // console.log(response); 108 | return response.json(); 109 | }; 110 | 111 | export const accept = async (groupId, userId) => { 112 | const token = await SInfo.getItem('authToken', {}); 113 | const response = await fetch(ACCEPT_REQUEST(groupId), { 114 | method: 'PUT', 115 | headers: { 116 | 'Content-Type': 'application/json', 117 | Authorization: token, 118 | }, 119 | body: JSON.stringify({ 120 | user_id: userId, 121 | }), 122 | }); 123 | return response.json(); 124 | }; 125 | 126 | export const remove = async (groupId, userId) => { 127 | const token = await SInfo.getItem('authToken', {}); 128 | const response = await fetch(REMOVE_USER(groupId), { 129 | method: 'PUT', 130 | headers: { 131 | 'Content-Type': 'application/json', 132 | Authorization: token, 133 | }, 134 | body: JSON.stringify({ 135 | user_id: userId, 136 | }), 137 | }); 138 | return response.json(); 139 | }; 140 | 141 | export const assign = async (groupId, userId) => { 142 | const token = await SInfo.getItem('authToken', {}); 143 | // console.log('URL ', ASSIGN_GROUP_ADMIN(groupId)); 144 | const response = await fetch(ASSIGN_GROUP_ADMIN(groupId), { 145 | method: 'PUT', 146 | headers: { 147 | 'Content-Type': 'application/json', 148 | Authorization: token, 149 | }, 150 | body: JSON.stringify({ 151 | user_id: userId, 152 | }), 153 | }); 154 | // console.log(response); 155 | return response.json(); 156 | }; 157 | 158 | export const edit = async (groupId, formdata) => { 159 | const token = await SInfo.getItem('authToken', {}); 160 | const response = await fetch(EDIT_GROUP(groupId), { 161 | method: 'PUT', 162 | headers: { 163 | 'Content-Type': 'application/json', 164 | Authorization: token, 165 | }, 166 | body: formdata, 167 | }); 168 | return response.json(); 169 | }; 170 | -------------------------------------------------------------------------------- /src/redux/api/notifications.js: -------------------------------------------------------------------------------- 1 | // Add auth token to all authorization headers 2 | import { 3 | GET_ALL_NOTIFICATIONS, 4 | GET_AVAILABLE_NOTIFICATIONS, 5 | MARK_NOTIFICATION_READ, 6 | REGISTER_DEVICE, 7 | EDIT_DEVICE, 8 | DELETE_DEVICE, 9 | } from '../../constants/urls'; 10 | import SInfo from 'react-native-sensitive-info'; 11 | 12 | export const getAll = async (userId, page) => { 13 | const token = await SInfo.getItem('authToken', {}); 14 | const response = await fetch(GET_ALL_NOTIFICATIONS(userId, page), { 15 | headers: { 16 | Authorization: token, 17 | }, 18 | }); 19 | // console.log(response); 20 | return response.json(); 21 | }; 22 | 23 | export const mark = async notifId => { 24 | const token = await SInfo.getItem('authToken', {}); 25 | const response = await fetch(MARK_NOTIFICATION_READ(notifId), { 26 | method: 'PUT', 27 | headers: { 28 | Authorization: token, 29 | }, 30 | }); 31 | return response.json(); 32 | }; 33 | 34 | export const getAvailable = async () => { 35 | const token = await SInfo.getItem('authToken', {}); 36 | const response = await fetch(GET_AVAILABLE_NOTIFICATIONS, { 37 | headers: { 38 | Authorization: token, 39 | }, 40 | }); 41 | return response.json(); 42 | }; 43 | 44 | // DEVICES 45 | 46 | export const register = async (type, deviceToken) => { 47 | const token = await SInfo.getItem('authToken', {}); 48 | // console.log('AUTH TOKEN ', token); 49 | // console.log('URL ', REGISTER_DEVICE); 50 | const response = await fetch(REGISTER_DEVICE, { 51 | method: 'POST', 52 | headers: { 53 | 'Content-Type': 'application/json', 54 | Authorization: token, 55 | }, 56 | body: JSON.stringify({ 57 | is_type: type, 58 | device_token: deviceToken, 59 | }), 60 | }); 61 | return response.json(); 62 | }; 63 | 64 | export const editBadge = async (type, badgeCount) => { 65 | const token = await SInfo.getItem('authToken', {}); 66 | const deviceId = await SInfo.getItem('deviceId', {}); 67 | const deviceToken = await SInfo.getItem('deviceToken', {}); 68 | const response = await fetch(EDIT_DEVICE(parseInt(deviceId)), { 69 | method: 'PUT', 70 | headers: { 71 | 'Content-Type': 'application/json', 72 | Authorization: token, 73 | }, 74 | body: JSON.stringify({ 75 | is_type: type, 76 | device_token: deviceToken, 77 | notification_count: badgeCount, 78 | }), 79 | }); 80 | return response.json(); 81 | }; 82 | 83 | export const editToken = async (type, deviceToken) => { 84 | const token = await SInfo.getItem('authToken', {}); 85 | const deviceId = await SInfo.getItem('deviceId', {}); 86 | // const deviceToken = await SInfo.getItem('deviceToken', {}); 87 | const response = await fetch(EDIT_DEVICE(parseInt(deviceId)), { 88 | method: 'PUT', 89 | headers: { 90 | 'Content-Type': 'application/json', 91 | Authorization: token, 92 | }, 93 | body: JSON.stringify({ 94 | is_type: type, 95 | device_token: deviceToken, 96 | }), 97 | }); 98 | return response.json(); 99 | }; 100 | 101 | export const deleteDevice = async () => { 102 | const token = await SInfo.getItem('authToken', {}); 103 | const deviceId = await SInfo.getItem('deviceId', {}); 104 | const response = await fetch(DELETE_DEVICE(parseInt(deviceId)), { 105 | method: 'DELETE', 106 | headers: { 107 | Authorization: token, 108 | }, 109 | }); 110 | return response.json(); 111 | }; 112 | -------------------------------------------------------------------------------- /src/redux/api/organizations.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_ALL_ORGANIZATIONS, 3 | GET_SINGLE_ORGANIZATION, 4 | } from '../../constants/urls'; 5 | import SInfo from 'react-native-sensitive-info'; 6 | 7 | export const getAll = async () => { 8 | const response = await fetch(GET_ALL_ORGANIZATIONS()); 9 | return response.json(); 10 | }; 11 | 12 | export const getSingle = async orgId => { 13 | const token = await SInfo.getItem('authToken', {}); 14 | const response = await fetch(GET_SINGLE_ORGANIZATION(orgId), { 15 | headers: { 16 | 'Content-Type': 'application/json', 17 | Authorization: token, 18 | }, 19 | }); 20 | return response.json(); 21 | }; 22 | -------------------------------------------------------------------------------- /src/redux/api/posts.js: -------------------------------------------------------------------------------- 1 | // Add auth token to all authorization headers 2 | import { 3 | GET_ALL_POSTS_FROM_GROUP, 4 | GET_ALL_POSTS, 5 | CREATE_POST, 6 | UNLIKE_POST, 7 | LIKE_POST, 8 | DELETE_POST, 9 | GET_SINGLE_POST, 10 | EDIT_POST, 11 | CREATE_COMMENT_ON_POST, 12 | UNLIKE_COMMENT_ON_POST, 13 | LIKE_COMMENT_ON_POST, 14 | DELETE_COMMENT_ON_POST, 15 | EDIT_COMMENT_ON_POST, 16 | } from '../../constants/urls'; 17 | import SInfo from 'react-native-sensitive-info'; 18 | 19 | export const postGetAll = async page => { 20 | const orgId = await SInfo.getItem('defaultOrgId', {}); 21 | const token = await SInfo.getItem('authToken', {}); 22 | const response = await fetch(GET_ALL_POSTS(parseInt(orgId), page), { 23 | headers: { 24 | 'Content-Type': 'application/json', 25 | Authorization: token, 26 | }, 27 | }); 28 | // console.log(response); 29 | return response.json(); 30 | }; 31 | 32 | export const postGetSingle = async postId => { 33 | const token = await SInfo.getItem('authToken', {}); 34 | const response = await fetch(GET_SINGLE_POST(postId), { 35 | headers: { 36 | 'Content-Type': 'application/json', 37 | Authorization: token, 38 | }, 39 | }); 40 | // console.log(response); 41 | return response.json(); 42 | }; 43 | 44 | export const postCreate = async formdata => { 45 | const orgId = await SInfo.getItem('defaultOrgId', {}); 46 | const token = await SInfo.getItem('authToken', {}); 47 | formdata.append('organization_id', parseInt(orgId)); 48 | 49 | const response = await fetch(CREATE_POST, { 50 | method: 'POST', 51 | headers: { 52 | Authorization: token, 53 | }, 54 | body: formdata, 55 | }); 56 | // console.log(response); 57 | return response.json(); 58 | }; 59 | 60 | export const postEdit = async (postId, formdata) => { 61 | // console.log('EDIT POST', postId, formdata); 62 | const token = await SInfo.getItem('authToken', {}); 63 | const response = await fetch(EDIT_POST(postId), { 64 | method: 'PUT', 65 | headers: { 66 | Authorization: token, 67 | }, 68 | body: formdata, 69 | }); 70 | // console.log(response); 71 | return response.json(); 72 | }; 73 | 74 | export const postLike = async postId => { 75 | const token = await SInfo.getItem('authToken', {}); 76 | const response = await fetch(LIKE_POST(postId), { 77 | method: 'PUT', 78 | headers: { 79 | 'Content-Type': 'application/json', 80 | Authorization: token, 81 | }, 82 | }); 83 | return response.json(); 84 | }; 85 | 86 | export const postUnlike = async postId => { 87 | const token = await SInfo.getItem('authToken', {}); 88 | const response = await fetch(UNLIKE_POST(postId), { 89 | method: 'PUT', 90 | headers: { 91 | 'Content-Type': 'application/json', 92 | Authorization: token, 93 | }, 94 | }); 95 | return response.json(); 96 | }; 97 | 98 | export const postDestroy = async postId => { 99 | const token = await SInfo.getItem('authToken', {}); 100 | const response = await fetch(DELETE_POST(postId), { 101 | method: 'DELETE', 102 | headers: { 103 | 'Content-Type': 'application/json', 104 | Authorization: token, 105 | }, 106 | }); 107 | // console.log(response); 108 | return response.json(); 109 | }; 110 | 111 | // COMMENTS 112 | 113 | export const commentCreate = async (postId, content) => { 114 | const token = await SInfo.getItem('authToken', {}); 115 | const response = await fetch(CREATE_COMMENT_ON_POST(postId), { 116 | method: 'POST', 117 | headers: { 118 | 'Content-Type': 'application/json', 119 | Authorization: token, 120 | }, 121 | body: JSON.stringify({ 122 | content, 123 | }), 124 | }); 125 | return response.json(); 126 | }; 127 | 128 | export const commentEdit = async (postId, commentId, content) => { 129 | const token = await SInfo.getItem('authToken', {}); 130 | const response = await fetch(EDIT_COMMENT_ON_POST(postId, commentId), { 131 | method: 'PUT', 132 | headers: { 133 | 'Content-Type': 'application/json', 134 | Authorization: token, 135 | }, 136 | body: JSON.stringify({ 137 | content, 138 | }), 139 | }); 140 | return response.json(); 141 | }; 142 | 143 | export const commentLike = async (postId, commentId) => { 144 | const token = await SInfo.getItem('authToken', {}); 145 | const response = await fetch(LIKE_COMMENT_ON_POST(postId, commentId), { 146 | method: 'PUT', 147 | headers: { 148 | 'Content-Type': 'application/json', 149 | Authorization: token, 150 | }, 151 | }); 152 | return response.json(); 153 | }; 154 | 155 | export const commentUnlike = async (postId, commentId) => { 156 | const token = await SInfo.getItem('authToken', {}); 157 | const response = await fetch(UNLIKE_COMMENT_ON_POST(postId, commentId), { 158 | method: 'PUT', 159 | headers: { 160 | 'Content-Type': 'application/json', 161 | Authorization: token, 162 | }, 163 | }); 164 | return response.json(); 165 | }; 166 | 167 | export const commentDestroy = async (postId, commentId) => { 168 | const token = await SInfo.getItem('authToken', {}); 169 | const response = await fetch(DELETE_COMMENT_ON_POST(postId, commentId), { 170 | method: 'DELETE', 171 | headers: { 172 | 'Content-Type': 'application/json', 173 | Authorization: token, 174 | }, 175 | }); 176 | return response.json(); 177 | }; 178 | -------------------------------------------------------------------------------- /src/redux/api/resources.js: -------------------------------------------------------------------------------- 1 | // Add auth token to all authorization headers 2 | import { GET_ALL_RESOURCES } from '../../constants/urls'; 3 | import SInfo from 'react-native-sensitive-info'; 4 | 5 | export const getAll = async () => { 6 | const orgId = await SInfo.getItem('defaultOrgId', {}); 7 | const token = await SInfo.getItem('authToken', {}); 8 | const response = await fetch(GET_ALL_RESOURCES(parseInt(orgId)), { 9 | headers: { 10 | 'Content-Type': 'application/json', 11 | Authorization: token, 12 | }, 13 | }); 14 | return response.json(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/redux/api/users.js: -------------------------------------------------------------------------------- 1 | import { 2 | SIGN_UP, 3 | SIGN_IN, 4 | FORGOT_PASSWORD, 5 | SIGN_OUT, 6 | UPDATE_PROFILE, 7 | GET_CURRENT_USER, 8 | GET_SINGLE_USER, 9 | GET_NOTIFICATIONS_FOR_USER, 10 | TOGGLE_NOTIFICATION, 11 | } from '../../constants/urls'; 12 | import SInfo from 'react-native-sensitive-info'; 13 | 14 | export const signUp = async ({ 15 | organization_id, 16 | organization_name, 17 | organization_url, 18 | organization_phone, 19 | first_name, 20 | last_name, 21 | email, 22 | password, 23 | }) => { 24 | const response = await fetch(SIGN_UP, { 25 | method: 'POST', 26 | headers: { 27 | 'Content-Type': 'application/json', 28 | }, 29 | body: JSON.stringify({ 30 | organization_id, 31 | organization_name, 32 | organization_url, 33 | organization_phone, 34 | first_name, 35 | last_name, 36 | email, 37 | password, 38 | }), 39 | }); 40 | return response.json(); 41 | }; 42 | 43 | export const emailAuth = async (email, password) => { 44 | // console.log('CALLING SIGN IN '); 45 | const response = await fetch(SIGN_IN, { 46 | method: 'POST', 47 | headers: { 48 | 'Content-Type': 'application/json', 49 | }, 50 | body: JSON.stringify({ 51 | email, 52 | password, 53 | }), 54 | }); 55 | return response.json(); 56 | }; 57 | 58 | export const signOut = async () => { 59 | const response = await fetch(SIGN_OUT, { 60 | method: 'POST', 61 | headers: { 62 | 'Content-Type': 'application/json', 63 | }, 64 | }); 65 | return response.json(); 66 | }; 67 | 68 | export const forgotPassword = async email => { 69 | const response = await fetch(FORGOT_PASSWORD, { 70 | method: 'POST', 71 | headers: { 72 | 'Content-Type': 'application/json', 73 | }, 74 | body: JSON.stringify({ 75 | email, 76 | }), 77 | }); 78 | return response.json(); 79 | }; 80 | 81 | export const get = async () => { 82 | const token = await SInfo.getItem('authToken', {}); 83 | const response = await fetch(GET_CURRENT_USER, { 84 | headers: { 85 | 'Content-Type': 'application/json', 86 | Authorization: token, 87 | }, 88 | }); 89 | return response.json(); 90 | }; 91 | 92 | export const getSingle = async userId => { 93 | const token = await SInfo.getItem('authToken', {}); 94 | const response = await fetch(GET_SINGLE_USER(userId), { 95 | headers: { 96 | 'Content-Type': 'application/json', 97 | Authorization: token, 98 | }, 99 | }); 100 | return response.json(); 101 | }; 102 | 103 | export const getNotifications = async userId => { 104 | const token = await SInfo.getItem('authToken', {}); 105 | const response = await fetch(GET_NOTIFICATIONS_FOR_USER(userId), { 106 | headers: { 107 | 'Content-Type': 'application/json', 108 | Authorization: token, 109 | }, 110 | }); 111 | return response.json(); 112 | }; 113 | 114 | export const toggle = async (userId, notification) => { 115 | const token = await SInfo.getItem('authToken', {}); 116 | const response = await fetch(TOGGLE_NOTIFICATION(userId), { 117 | method: 'PUT', 118 | headers: { 119 | 'Content-Type': 'application/json', 120 | Authorization: token, 121 | }, 122 | body: JSON.stringify({ 123 | notification_name: `${notification}`, 124 | }), 125 | }); 126 | return response.json(); 127 | }; 128 | 129 | export const update = async (userId, formData) => { 130 | const token = await SInfo.getItem('authToken', {}); 131 | const response = await fetch(UPDATE_PROFILE(userId), { 132 | method: 'PUT', 133 | headers: { 134 | 'Content-Type': 'application/json', 135 | Authorization: token, 136 | }, 137 | body: formData, 138 | }); 139 | return response.json(); 140 | }; 141 | -------------------------------------------------------------------------------- /src/redux/reducers/categories.reducer.js: -------------------------------------------------------------------------------- 1 | import { CATEGORIES } from '../actions/categories.actions'; 2 | 3 | // Initial state 4 | const initialState = { status: 'pending' }; 5 | 6 | // Reducer 7 | export default function categories(state = initialState, action) { 8 | switch (action.type) { 9 | case CATEGORIES: 10 | return { ...state, ...action.payload }; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/redux/reducers/conversations.reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | CONVERSATIONS, 3 | NEW_CONVERSATION, 4 | } from '../actions/conversations.actions'; 5 | 6 | // Initial state 7 | const initialState = { status: 'pending' }; 8 | 9 | // Reducer 10 | export default function conversations(state = initialState, action) { 11 | switch (action.type) { 12 | case CONVERSATIONS: 13 | return { ...state, ...action.payload }; 14 | case NEW_CONVERSATION: 15 | return action.payload; 16 | default: 17 | return state; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/redux/reducers/groups.reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | GROUPS, 3 | SINGLE_GROUP, 4 | CLEAR_GROUP, 5 | ADD_TO_GROUP, 6 | ASSIGN_ADMIN, 7 | CREATE_GROUP, 8 | LEAVE_GROUP, 9 | } from '../actions/groups.actions'; 10 | // Initial state 11 | const initialState = { status: 'pending' }; 12 | 13 | // Reducer 14 | export default function groups(state = initialState, action) { 15 | switch (action.type) { 16 | case GROUPS: 17 | if (action.payload.currentPage !== 1) { 18 | return { 19 | status: action.payload.status, 20 | results: [...state.results, ...action.payload.results], 21 | currentPage: action.payload.currentPage, 22 | allPages: action.payload.allPages, 23 | }; 24 | } else { 25 | return { ...state, ...action.payload }; 26 | } 27 | case LEAVE_GROUP: 28 | return action.payload; 29 | case CREATE_GROUP: 30 | return action.payload; 31 | case SINGLE_GROUP: 32 | return action.payload; 33 | case CLEAR_GROUP: 34 | return action.payload; 35 | case ADD_TO_GROUP: 36 | return action.payload; 37 | case ASSIGN_ADMIN: 38 | return action.payload; 39 | default: 40 | return state; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import categories from './categories.reducer'; 3 | import conversations from './conversations.reducer'; 4 | import groups from './groups.reducer'; 5 | import loginType from './loginType.reducer'; 6 | import messageIndicator from './messageIndicator.reducer'; 7 | import notifications from './notifications.reducer'; 8 | import notificationIndicator from './notificationIndicator.reducer'; 9 | import organizations from './organizations.reducer'; 10 | import otherUsers from './otherUsers.reducer'; 11 | import posts from './posts.reducer'; 12 | import resources from './resources.reducer'; 13 | import tabBar from './tabBar.reducer'; 14 | import singlePost from './singlePost.reducer'; 15 | import user from './users.reducer'; 16 | 17 | const rootReducer = combineReducers({ 18 | categories, 19 | conversations, 20 | groups, 21 | loginType, 22 | messageIndicator, 23 | notifications, 24 | notificationIndicator, 25 | organizations, 26 | otherUsers, 27 | posts, 28 | resources, 29 | tabBar, 30 | singlePost, 31 | user, 32 | }); 33 | 34 | export default rootReducer; 35 | -------------------------------------------------------------------------------- /src/redux/reducers/loginType.reducer.js: -------------------------------------------------------------------------------- 1 | import { SET_LOGIN_TYPE } from '../actions/loginType.actions'; 2 | 3 | export default function(state = null, action) { 4 | switch (action.type) { 5 | default: 6 | return state; 7 | case SET_LOGIN_TYPE: 8 | return action.payload; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/redux/reducers/messageIndicator.reducer.js: -------------------------------------------------------------------------------- 1 | import { MESSAGE_INDICATOR } from '../actions/messageIndicator.actions'; 2 | 3 | const initialState = { shouldShow: false }; 4 | 5 | export default function(state = initialState, action) { 6 | switch (action.type) { 7 | default: 8 | return state; 9 | case MESSAGE_INDICATOR: 10 | return action.payload; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/redux/reducers/notificationIndicator.reducer.js: -------------------------------------------------------------------------------- 1 | import { NOTIFICATION_INDICATOR } from '../actions/notificationIndicator.actions'; 2 | 3 | const initialState = { shouldShow: false }; 4 | 5 | export default function(state = initialState, action) { 6 | switch (action.type) { 7 | default: 8 | return state; 9 | case NOTIFICATION_INDICATOR: 10 | return action.payload; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/redux/reducers/notifications.reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | NOTIFICATIONS, 3 | MARK_READ, 4 | AVAILABLE_NOTIFICATIONS, 5 | REGISTERED_DEVICE, 6 | } from '../actions/notifications.actions'; 7 | 8 | // Initial state 9 | const initialState = { status: 'pending' }; 10 | 11 | // Reducer 12 | export default function notifications(state = initialState, action) { 13 | switch (action.type) { 14 | case NOTIFICATIONS: 15 | if (action.payload.currentPage !== 1) { 16 | return { 17 | status: action.payload.status, 18 | results: [...state.results, ...action.payload.results], 19 | currentPage: action.payload.currentPage, 20 | allPages: action.payload.allPages, 21 | }; 22 | } else { 23 | return { ...state, ...action.payload }; 24 | } 25 | case MARK_READ: 26 | return action.payload; 27 | case AVAILABLE_NOTIFICATIONS: 28 | return action.payload; 29 | case REGISTERED_DEVICE: 30 | return action.payload; 31 | default: 32 | return state; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/redux/reducers/organizations.reducer.js: -------------------------------------------------------------------------------- 1 | import { ORGANIZATIONS } from '../actions/organizations.actions'; 2 | 3 | // Initial state 4 | const initialState = { status: 'pending' }; 5 | 6 | // Reducer 7 | export default function organizations(state = initialState, action) { 8 | switch (action.type) { 9 | case ORGANIZATIONS: 10 | return { ...state, ...action.payload }; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/redux/reducers/otherUsers.reducer.js: -------------------------------------------------------------------------------- 1 | import { OTHER_USER } from '../actions/users.actions'; 2 | 3 | // Initial state 4 | const initialState = { auth: 'failed', error: '', message: '' }; 5 | 6 | // Reducer 7 | export default function user(state = initialState, action) { 8 | switch (action.type) { 9 | case OTHER_USER: 10 | return { ...state, ...action.payload }; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/redux/reducers/posts.reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | POSTS, 3 | SINGLE_POST, 4 | LIKE_POST, 5 | UNLIKE_POST, 6 | COMMENT_POST, 7 | LIKE_COMMENT, 8 | UNLIKE_COMMENT, 9 | NEW_POST, 10 | EDIT_POST, 11 | DELETE_POST, 12 | EDIT_COMMENT, 13 | DELETE_COMMENT, 14 | } from '../actions/posts.actions'; 15 | 16 | // Initial state 17 | const initialState = { status: 'pending', currentPage: 1, allPages: 1 }; 18 | 19 | // Reducer 20 | export default function posts(state = initialState, action) { 21 | switch (action.type) { 22 | case POSTS: 23 | // console.log('STATE ', state.results); 24 | // console.log('PAYLOAD ', action.payload.results); 25 | if (action.payload.currentPage !== 1) { 26 | return { 27 | status: action.payload.status, 28 | results: [...state.results, ...action.payload.results], 29 | currentPage: action.payload.currentPage, 30 | allPages: action.payload.allPages, 31 | }; 32 | } else { 33 | return { ...state, ...action.payload }; 34 | } 35 | case SINGLE_POST: 36 | return { status: action.payload.status, results: action.payload.results }; 37 | case NEW_POST: 38 | return { 39 | ...state, 40 | status: action.payload.status, 41 | results: action.payload.results, 42 | }; 43 | case EDIT_POST: 44 | return { 45 | ...state, 46 | status: action.payload.status, 47 | results: action.payload.results, 48 | }; 49 | case DELETE_POST: 50 | return { 51 | ...state, 52 | status: action.payload.status, 53 | results: action.payload.results, 54 | }; 55 | case LIKE_POST: 56 | // const likedElementPos = state.results 57 | // .map(x => { 58 | // return x.id; 59 | // }) 60 | // .indexOf(action.payload.results.id); 61 | 62 | // const updatedFromLike = state.results; 63 | // Updated post is in action.payload.results 64 | // updatedFromLike.splice(likedElementPos, 1, action.payload.results); 65 | 66 | // Return updated post 67 | return { 68 | ...state, 69 | status: action.payload.status, 70 | results: action.payload.results, 71 | }; 72 | case UNLIKE_POST: 73 | // const unlikedElementPos = state.results 74 | // .map(x => { 75 | // return x.id; 76 | // }) 77 | // .indexOf(action.payload.results.id); 78 | 79 | // Remove reaction from current user from posts reactions array 80 | const filteredReactions = action.payload.results.reactions.filter( 81 | reaction => reaction.user.id !== action.payload.userId, 82 | ); 83 | // Create new post with updated reactions 84 | const updatedPost = { 85 | ...action.payload.results, 86 | reactions: filteredReactions, 87 | }; 88 | 89 | // const updatedFromUnliked = state.results; 90 | // updatedFromUnliked.splice(unlikedElementPos, 1, updatedPost); 91 | // // Create new posts array with other posts and updated post 92 | return { 93 | ...state, 94 | status: action.payload.status, 95 | results: updatedPost, 96 | }; 97 | case COMMENT_POST: 98 | // // Get other posts that didn't have the reaction added 99 | // const filteredCommentPosts = state.results.filter( 100 | // post => post.id !== action.payload.results.id, 101 | // ); 102 | // Return posts array with updated post added (new comment is added in action created from response) 103 | return { 104 | ...state, 105 | status: action.payload.status, 106 | results: action.payload.results, 107 | }; 108 | case LIKE_COMMENT: 109 | // Add new reaction to comments array on post, return post and comment 110 | return { 111 | ...state, 112 | status: action.payload.status, 113 | results: action.payload.results, 114 | comment: { 115 | ...action.payload.comment, 116 | reactions: [ 117 | ...action.payload.comment.reactions, 118 | action.payload.newReaction, 119 | ], 120 | }, 121 | }; 122 | case UNLIKE_COMMENT: 123 | // console.log('Payload ', action.payload); 124 | // Remove reaction from current user from comments reactions array 125 | const filteredReactionsToComment = action.payload.comment.reactions.filter( 126 | reaction => reaction.user.id !== action.payload.userId, 127 | ); 128 | return { 129 | ...state, 130 | status: action.payload.status, 131 | results: action.payload.results, 132 | comment: { 133 | ...action.payload.comment, 134 | reactions: filteredReactionsToComment, 135 | }, 136 | }; 137 | case EDIT_COMMENT: 138 | return { 139 | ...state, 140 | status: action.payload.status, 141 | results: action.payload.results, 142 | }; 143 | case DELETE_COMMENT: 144 | return { 145 | ...state, 146 | status: action.payload.status, 147 | results: action.payload.results, 148 | }; 149 | default: 150 | return state; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/redux/reducers/resources.reducer.js: -------------------------------------------------------------------------------- 1 | import { RESOURCES } from '../actions/resources.actions'; 2 | 3 | // Initial state 4 | const initialState = { status: 'pending' }; 5 | 6 | // Reducer 7 | export default function resources(state = initialState, action) { 8 | switch (action.type) { 9 | case RESOURCES: 10 | return { ...state, ...action.payload }; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/redux/reducers/singlePost.reducer.js: -------------------------------------------------------------------------------- 1 | import { SINGLE_POST } from '../actions/posts.actions'; 2 | 3 | // Initial state 4 | const initialState = { status: 'pending' }; 5 | 6 | // Reducer 7 | export default function singlePost(state = initialState, action) { 8 | switch (action.type) { 9 | case SINGLE_POST: 10 | return { ...state, ...action.payload }; 11 | // case LIKE_POST: 12 | // const filteredPosts = state.results.filter( 13 | // post => post.id !== action.payload.results.id, 14 | // ); 15 | // return { 16 | // ...state, 17 | // results: [action.payload.results, ...filteredPosts], 18 | // }; 19 | default: 20 | return state; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/redux/reducers/tabBar.reducer.js: -------------------------------------------------------------------------------- 1 | import { SELECTED_TAB } from '../actions/tabBar.actions'; 2 | 3 | const initialState = { selectedTab: 'MainFeed' }; 4 | 5 | export default function(state = initialState, action) { 6 | switch (action.type) { 7 | default: 8 | return state; 9 | case SELECTED_TAB: 10 | return action.payload; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/redux/reducers/users.reducer.js: -------------------------------------------------------------------------------- 1 | import { AUTHORIZED_USER, LOGOUT_USER } from '../actions/users.actions'; 2 | 3 | // Initial state 4 | const initialState = { auth: 'failed', error: '' }; 5 | 6 | // Reducer 7 | export default function user(state = initialState, action) { 8 | switch (action.type) { 9 | case AUTHORIZED_USER: 10 | return { ...state, ...action.payload }; 11 | 12 | case LOGOUT_USER: 13 | return initialState; 14 | 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/screens/ForgotPasswordScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | KeyboardAvoidingView, 4 | SafeAreaView, 5 | Dimensions, 6 | Platform, 7 | } from 'react-native'; 8 | import { connect } from 'react-redux'; 9 | import styled from 'styled-components/native'; 10 | import LargeButton from '../components/LargeButton'; 11 | import colors from '../components/Global/colors'; 12 | import Success from '../assets/images/Success.png'; 13 | import { sendPassword } from '../redux/actions/users.actions'; 14 | import { StyledInput } from './SignInScreen'; 15 | import BackArrow from '../assets/images/BackArrow.png'; 16 | import { StyledLeftButton, StyledIcon } from '../components/HeaderDetail'; 17 | 18 | const DEVICE_WIDTH = Dimensions.get('window').width; 19 | const StyledError = styled.Text` 20 | color: ${colors.error}; 21 | font-style: normal; 22 | font-weight: 600; 23 | line-height: 14px; 24 | font-size: 12px; 25 | letter-spacing: -0.0738462px; 26 | margin-top: 20; 27 | `; 28 | 29 | const Heading = styled.Text` 30 | font-weight: 600; 31 | line-height: 26px; 32 | font-size: 16px; 33 | letter-spacing: -0.0861539px; 34 | color: ${colors.white}; 35 | margin-top: 75; 36 | margin-bottom: 60; 37 | `; 38 | 39 | const StyledSuccessImage = styled.Image` 40 | align-self: center; 41 | margin-top: 50; 42 | `; 43 | 44 | const StyledHeader = styled.View` 45 | width: ${DEVICE_WIDTH}; 46 | height: 120; 47 | background-color: ${colors.darkerBase}; 48 | justify-content: center; 49 | padding-left: 10; 50 | `; 51 | 52 | const StyledContainer = styled.View` 53 | background-color: ${colors.primary}; 54 | align-self: stretch; 55 | height: 100%; 56 | padding-left: 10px; 57 | padding-right: 10px; 58 | align-items: center; 59 | `; 60 | 61 | const StyledHeaderTitle = styled.Text` 62 | font-weight: bold; 63 | font-size: 22px; 64 | letter-spacing: -0.0861539px; 65 | color: ${colors.white}; 66 | margin-top: 23; 67 | `; 68 | 69 | class ForgotPasswordScreen extends Component { 70 | constructor() { 71 | super(); 72 | 73 | this.state = { 74 | email: '', 75 | error: '', 76 | emailSent: false, 77 | }; 78 | } 79 | 80 | componentDidUpdate(prevProps) { 81 | const { user } = this.props; 82 | if (prevProps.user.auth === 'pending' && user.auth === 'complete') { 83 | this.setState({ emailSent: true }); 84 | } else if ( 85 | user.auth === 'failed' && 86 | user.error === 'not found' && 87 | prevProps.user.error !== 'not found' 88 | ) { 89 | this.handleError('Email not found. Do you need to sign up?'); 90 | } 91 | } 92 | 93 | handleLeftHeaderButton = () => { 94 | this.props.navigation.goBack(); 95 | }; 96 | 97 | handleError = error => { 98 | this.setState({ error }); 99 | }; 100 | 101 | handleSendPassword = () => { 102 | this.setState({ error: '' }, () => { 103 | if (!this.state.email) { 104 | this.setState({ error: 'Please enter an email' }); 105 | } 106 | if (this.state.email) { 107 | this.props.sendPassword(this.state.email); 108 | } 109 | }); 110 | }; 111 | 112 | render() { 113 | return ( 114 | 115 | 116 | 117 | 118 | 119 | 120 | Forgot Your Password? 121 | 122 | 123 | {this.state.emailSent && } 124 | 125 | {this.state.emailSent 126 | ? 'Email sent! Check your email and follow the password reset instructions.' 127 | : 'Enter your email below to receive your password reset instructions.'} 128 | 129 | {!this.state.emailSent && ( 130 | 135 | this.setState({ email })} 140 | placeholderTextColor={colors.white} 141 | autoCapitalize="none" 142 | /> 143 | 156 | {this.state.error !== '' && ( 157 | {this.state.error} 158 | )} 159 | 160 | )} 161 | 162 | 163 | ); 164 | } 165 | } 166 | 167 | const mapStateToProps = state => ({ 168 | user: state.user, 169 | }); 170 | 171 | export default connect( 172 | mapStateToProps, 173 | { sendPassword }, 174 | )(ForgotPasswordScreen); 175 | -------------------------------------------------------------------------------- /src/screens/LaunchScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { ActivityIndicator } from 'react-native'; 3 | import SInfo from 'react-native-sensitive-info'; 4 | import { connect } from 'react-redux'; 5 | import styled from 'styled-components/native'; 6 | import { signInUser } from '../redux/actions/users.actions'; 7 | import { setLoginType } from '../redux/actions/loginType.actions'; 8 | import { resetStack } from '../util/helpers'; 9 | import colors from '../components/Global/colors'; 10 | import LogoIcon from '../components/LogoIcon'; 11 | 12 | const StyledView = styled.View` 13 | flex: 1; 14 | align-items: center; 15 | justify-content: center; 16 | background-color: ${colors.primary}; 17 | `; 18 | 19 | const Heading = styled.Text` 20 | font-style: normal; 21 | font-weight: bold; 22 | line-height: 45px; 23 | font-size: 34px; 24 | letter-spacing: -0.233846px; 25 | color: ${colors.white}; 26 | `; 27 | 28 | const StyledHeadingView = styled.View` 29 | flex-direction: row; 30 | align-items: center; 31 | align-self: center; 32 | height: 45; 33 | `; 34 | 35 | const StyledLogoView = styled.View` 36 | margin-right: 10; 37 | `; 38 | 39 | class LaunchScreen extends Component { 40 | componentDidMount() { 41 | this.attemptAutoLogin(); 42 | } 43 | 44 | componentDidUpdate(prevProps) { 45 | const { loginType, user, navigation } = this.props; 46 | // Only handle login from here if login type is auto login 47 | if (loginType === 'AUTO_LOGIN') { 48 | // If the user auth property comes back as successfull, send to Signed In 49 | // Or send to the Sign In screen 50 | if ( 51 | prevProps.user.auth !== user.auth && 52 | user.auth.includes('emailAuthComplete') 53 | ) { 54 | navigation.navigate('SignedIn'); 55 | } else if (user.auth === 'failed') { 56 | resetStack(navigation, 'SignIn', 1000); 57 | } 58 | } 59 | } 60 | 61 | attemptAutoLogin = async () => { 62 | // Get saved information and send to signInUser endpoint 63 | const email = await SInfo.getItem('email', {}); 64 | const password = await SInfo.getItem('password', {}); 65 | 66 | if (email && password) { 67 | // To simulate successfull login 68 | setTimeout(() => this.props.navigation.navigate('SignedIn'), 2000); 69 | } else { 70 | // To simulate failed login 71 | resetStack(this.props.navigation, 'SignIn', 2000); 72 | } 73 | 74 | // this.props.signInUser(email, password); 75 | 76 | // // Set login type of auto login 77 | // this.props.setLoginType('AUTO_LOGIN'); 78 | }; 79 | 80 | render() { 81 | return ( 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | RNTemplate 90 | 91 | 92 | 97 | 98 | ); 99 | } 100 | } 101 | 102 | const mapStateToProps = state => ({ 103 | user: state.user, 104 | loginType: state.loginType, 105 | }); 106 | 107 | export default connect( 108 | mapStateToProps, 109 | { 110 | signInUser, 111 | setLoginType, 112 | }, 113 | )(LaunchScreen); 114 | -------------------------------------------------------------------------------- /src/screens/MainScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components/native'; 3 | import colors from '../components/Global/colors'; 4 | import LogoIcon from '../components/LogoIcon'; 5 | 6 | const StyledView = styled.View` 7 | flex: 1; 8 | align-items: center; 9 | justify-content: center; 10 | background-color: ${colors.primary}; 11 | `; 12 | 13 | const Heading = styled.Text` 14 | font-style: normal; 15 | font-weight: bold; 16 | line-height: 45px; 17 | font-size: 34px; 18 | letter-spacing: -0.233846px; 19 | color: ${colors.white}; 20 | `; 21 | 22 | const StyledHeadingView = styled.View` 23 | flex-direction: row; 24 | align-items: center; 25 | align-self: center; 26 | height: 45; 27 | `; 28 | 29 | const StyledLogoView = styled.View` 30 | margin-right: 10; 31 | `; 32 | 33 | function MainScreen() { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Welcome! 43 | 44 | 45 | 46 | ); 47 | } 48 | 49 | export default MainScreen; 50 | -------------------------------------------------------------------------------- /src/screens/ProfileScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import moment from 'moment'; 3 | import styled from 'styled-components/native'; 4 | import LayoutScrollViewWithHeader from '../components/LayoutScrollViewWithHeader'; 5 | import Avatar from '../components/Avatar'; 6 | import Label from '../components/Label'; 7 | import colors from '../components/Global/colors'; 8 | import AboutCard from '../components/AboutCard'; 9 | import SectionCard from '../components/SectionCard'; 10 | import SettingsIcon from '../assets/images/SettingsIcon.png'; 11 | import Profile from '../assets/images/pam-beesly.jpg' 12 | 13 | const fakeUser = { 14 | avatar_url: Profile, 15 | city: 'Scranton', 16 | created_at: '2019-02-28T16:21:31.390Z', 17 | date_of_birth: '1078-01-01T00:00:00.000Z', 18 | email: 'pam@scc.com', 19 | first_name: 'Pam', 20 | full_name: 'Pam Halpert', 21 | gender: 'Female', 22 | id: 84, 23 | is_super_admin: false, 24 | last_name: 'Halpert', 25 | organization_ids: [44], 26 | phone: '5555555532', 27 | state: 'PA', 28 | zip: '18503', 29 | }; 30 | 31 | const StyledColumnView = styled.View` 32 | flex-direction: column; 33 | margin-left: 10; 34 | `; 35 | 36 | const StyledRowView = styled.View` 37 | flex-direction: row; 38 | align-items: center; 39 | padding-top: 15; 40 | padding-bottom: 15; 41 | `; 42 | 43 | const StyledName = styled.Text` 44 | color: ${colors.black}; 45 | font-style: normal; 46 | font-weight: bold; 47 | font-size: 20px; 48 | letter-spacing: -0.0738462px; 49 | margin-bottom: 8; 50 | `; 51 | 52 | const StyledOrganization = styled.Text` 53 | color: ${colors.mediumNeutral}; 54 | font-weight: 600; 55 | line-height: 16px; 56 | font-size: 12px; 57 | letter-spacing: -0.0861539px; 58 | `; 59 | 60 | const StyledInfoView = styled.View``; 61 | 62 | const StyledButton = styled.TouchableOpacity` 63 | height: 35; 64 | display: flex; 65 | margin-left: -10; 66 | justify-content: center; 67 | background-color: ${colors.primary}; 68 | `; 69 | 70 | const StyledButtonText = styled.Text` 71 | font-style: normal; 72 | font-weight: 600; 73 | line-height: 18px; 74 | font-size: 12px; 75 | text-align: center; 76 | letter-spacing: -0.0861539px; 77 | color: ${colors.white}; 78 | `; 79 | 80 | class ProfileScreen extends Component { 81 | constructor(props) { 82 | super(props); 83 | 84 | this.state = { 85 | user: fakeUser, 86 | }; 87 | } 88 | 89 | handleFirstLargeButton = () => { 90 | this.props.navigation.navigate('EditProfile', { user: this.state.user }); 91 | }; 92 | 93 | verifyContent = val => { 94 | if (val !== null && val !== 'null' && val !== '') { 95 | return true; 96 | } 97 | return false; 98 | }; 99 | 100 | createInfoArray = (date_of_birth, city, state, zip, gender) => { 101 | // Get age 102 | const age = date_of_birth ? moment().diff(date_of_birth, 'years') : null; 103 | // Create location array 104 | const locArr = [city, state, zip]; 105 | const location = locArr 106 | .filter(loc => { 107 | return this.verifyContent(loc); 108 | }) 109 | .join(', '); 110 | // Create gender, age array 111 | const ageGenArr = [gender, age]; 112 | const ageGen = ageGenArr 113 | .filter(item => { 114 | return this.verifyContent(item); 115 | }) 116 | .join(', '); 117 | return [ageGen, location]; 118 | }; 119 | 120 | render() { 121 | const { 122 | first_name, 123 | last_name, 124 | full_name, 125 | avatar_url, 126 | date_of_birth, 127 | gender, 128 | city, 129 | state, 130 | zip, 131 | } = this.state.user; 132 | return ( 133 | 138 | this.props.navigation.navigate('Settings') 139 | } 140 | > 141 | 142 | 143 | console.log('selected user')} 149 | /> 150 | 151 | {full_name} 152 | Member since 2015 153 | 154 | 155 | this.handleFirstLargeButton()}> 156 | Edit My Profile 157 | 158 | 159 | 160 | 161 | 174 | 175 | ); 176 | } 177 | } 178 | 179 | export default ProfileScreen; 180 | -------------------------------------------------------------------------------- /src/util/data.js: -------------------------------------------------------------------------------- 1 | const stateList = [ 2 | { id: 1, name: 'AL' }, 3 | { id: 2, name: 'AK' }, 4 | { id: 3, name: 'AZ' }, 5 | { id: 4, name: 'AR' }, 6 | { id: 5, name: 'CA' }, 7 | { id: 6, name: 'CO' }, 8 | { id: 7, name: 'CT' }, 9 | { id: 8, name: 'DE' }, 10 | { id: 9, name: 'FL' }, 11 | { id: 10, name: 'GA' }, 12 | { id: 11, name: 'HI' }, 13 | { id: 12, name: 'ID' }, 14 | { id: 13, name: 'IL' }, 15 | { id: 14, name: 'IN' }, 16 | { id: 15, name: 'IA' }, 17 | { id: 16, name: 'KS' }, 18 | { id: 17, name: 'KY' }, 19 | { id: 18, name: 'LA' }, 20 | { id: 19, name: 'ME' }, 21 | { id: 20, name: 'MD' }, 22 | { id: 21, name: 'MA' }, 23 | { id: 22, name: 'MI' }, 24 | { id: 23, name: 'MN' }, 25 | { id: 24, name: 'MS' }, 26 | { id: 25, name: 'MO' }, 27 | { id: 26, name: 'MT' }, 28 | { id: 27, name: 'NE' }, 29 | { id: 28, name: 'NV' }, 30 | { id: 29, name: 'NH' }, 31 | { id: 30, name: 'NJ' }, 32 | { id: 31, name: 'NM' }, 33 | { id: 32, name: 'NY' }, 34 | { id: 33, name: 'NC' }, 35 | { id: 34, name: 'ND' }, 36 | { id: 35, name: 'OH' }, 37 | { id: 36, name: 'OK' }, 38 | { id: 37, name: 'OR' }, 39 | { id: 38, name: 'PA' }, 40 | { id: 39, name: 'RI' }, 41 | { id: 40, name: 'SC' }, 42 | { id: 41, name: 'SD' }, 43 | { id: 42, name: 'TN' }, 44 | { id: 43, name: 'TX' }, 45 | { id: 44, name: 'UT' }, 46 | { id: 45, name: 'VT' }, 47 | { id: 46, name: 'VA' }, 48 | { id: 47, name: 'WA' }, 49 | { id: 48, name: 'WV' }, 50 | { id: 49, name: 'WI' }, 51 | { id: 50, name: 'WY' }, 52 | ]; 53 | 54 | export default stateList; 55 | -------------------------------------------------------------------------------- /src/util/helpers.js: -------------------------------------------------------------------------------- 1 | import { StackActions, NavigationActions } from 'react-navigation'; 2 | import SInfo from 'react-native-sensitive-info'; 3 | 4 | export function resetStack(navigation, routeName, delay) { 5 | setTimeout(() => { 6 | const resetAction = StackActions.reset({ 7 | index: 0, 8 | key: null, 9 | actions: [NavigationActions.navigate({ routeName })], 10 | }); 11 | navigation.dispatch(resetAction); 12 | }, delay); 13 | } 14 | 15 | export function truncateText(string, length) { 16 | if (string.length > length) { 17 | return string.substring(0, length) + '...'; 18 | } 19 | return string; 20 | } 21 | 22 | export function saveInfo(authToken, password, email, defaultOrgId) { 23 | SInfo.setItem('authToken', authToken, {}); 24 | SInfo.setItem('password', password, {}); 25 | SInfo.setItem('email', email, {}); 26 | SInfo.setItem('defaultOrgId', defaultOrgId, {}); 27 | } 28 | 29 | export function saveDeviceInfo(deviceId, deviceToken) { 30 | SInfo.setItem('deviceId', `${deviceId}`, {}); 31 | SInfo.setItem('deviceToken', deviceToken, {}); 32 | SInfo.setItem('hasPostedDeviceToken', deviceToken, {}); 33 | } 34 | --------------------------------------------------------------------------------