├── .buckconfig ├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── README.md ├── images ├── chat.png └── list.png ├── index.ios.js ├── ios ├── AwesomeProject.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── AwesomeProject.xcscheme ├── AwesomeProject │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── main.m └── AwesomeProjectTests │ ├── AwesomeProjectTests.m │ └── Info.plist ├── package.json └── src ├── App.js ├── colors.js ├── common ├── Avatar.js ├── BackButton.js ├── Badge.js ├── EmojiPicker.js ├── Header.js ├── IconButton.js └── Navbar.js ├── scene ├── Chat │ ├── BigEmoji.js │ ├── MessageBubble.js │ ├── MessageRow.js │ ├── Toolbar.js │ └── index.js └── Chats │ ├── ChatRow.js │ ├── SearchBar.js │ └── index.js ├── selectors.js ├── store.js └── util.js /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*[.]android.js 5 | 6 | # Ignore templates with `@flow` in header 7 | .*/local-cli/generator.* 8 | 9 | # Ignore malformed json 10 | .*/node_modules/y18n/test/.*\.json 11 | 12 | # Ignore the website subdir 13 | /website/.* 14 | 15 | # Ignore BUCK generated dirs 16 | /\.buckd/ 17 | 18 | # Ignore unexpected extra @providesModule 19 | .*/node_modules/commoner/test/source/widget/share.js 20 | 21 | # Ignore duplicate module providers 22 | # For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root 23 | .*/Libraries/react-native/React.js 24 | .*/Libraries/react-native/ReactNative.js 25 | .*/node_modules/jest-runtime/build/__tests__/.* 26 | 27 | [include] 28 | 29 | [libs] 30 | node_modules/react-native/Libraries/react-native/react-native-interface.js 31 | node_modules/react-native/flow 32 | flow/ 33 | 34 | [options] 35 | module.system=haste 36 | 37 | esproposal.class_static_fields=enable 38 | esproposal.class_instance_fields=enable 39 | 40 | experimental.strict_type_args=true 41 | 42 | munge_underscores=true 43 | 44 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 45 | 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' 46 | 47 | suppress_type=$FlowIssue 48 | suppress_type=$FlowFixMe 49 | suppress_type=$FixMe 50 | 51 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-2]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 52 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-2]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 53 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 54 | 55 | unsafe.enable_getters_and_setters=true 56 | 57 | [version] 58 | ^0.32.0 59 | -------------------------------------------------------------------------------- /.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/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | android/keystores/debug.keystore 42 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Messenger UI in React Native 2 | 3 | | ![](images/list.png) | ![](images/chat.png) | 4 | |---|---| 5 | 6 | Features a custom emoji picker as well. 7 | -------------------------------------------------------------------------------- /images/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goshacmd/messenger-ui-rn/dace450c00ceff2ff4941ed49475ef379f972e5c/images/chat.png -------------------------------------------------------------------------------- /images/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goshacmd/messenger-ui-rn/dace450c00ceff2ff4941ed49475ef379f972e5c/images/list.png -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { AppRegistry } from 'react-native'; 5 | import { Provider } from 'react-redux'; 6 | import { createStore } from 'redux'; 7 | 8 | import AwesomeProject from './src/App'; 9 | import { reducer } from './src/store'; 10 | 11 | const store = createStore(reducer); 12 | 13 | const App = () => ( 14 | 15 | 16 | 17 | ) 18 | 19 | AppRegistry.registerComponent('AwesomeProject', () => App); 20 | -------------------------------------------------------------------------------- /ios/AwesomeProject.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 11 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 12 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 13 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 14 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 15 | 00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */; }; 16 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 17 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 18 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; 19 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 20 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 21 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 22 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 23 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 24 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 25 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 26 | CAB1FB851DA80032007EE8BF /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAB1FB7B1DA8000C007EE8BF /* libRCTCameraRoll.a */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 33 | proxyType = 2; 34 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 35 | remoteInfo = RCTActionSheet; 36 | }; 37 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 40 | proxyType = 2; 41 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 42 | remoteInfo = RCTGeolocation; 43 | }; 44 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 47 | proxyType = 2; 48 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 49 | remoteInfo = RCTImage; 50 | }; 51 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { 52 | isa = PBXContainerItemProxy; 53 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 54 | proxyType = 2; 55 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 56 | remoteInfo = RCTNetwork; 57 | }; 58 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 59 | isa = PBXContainerItemProxy; 60 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 61 | proxyType = 2; 62 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 63 | remoteInfo = RCTVibration; 64 | }; 65 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { 66 | isa = PBXContainerItemProxy; 67 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; 68 | proxyType = 1; 69 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A; 70 | remoteInfo = AwesomeProject; 71 | }; 72 | 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { 73 | isa = PBXContainerItemProxy; 74 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 75 | proxyType = 2; 76 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 77 | remoteInfo = RCTSettings; 78 | }; 79 | 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { 80 | isa = PBXContainerItemProxy; 81 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 82 | proxyType = 2; 83 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A; 84 | remoteInfo = RCTWebSocket; 85 | }; 86 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 87 | isa = PBXContainerItemProxy; 88 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 89 | proxyType = 2; 90 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 91 | remoteInfo = React; 92 | }; 93 | 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { 94 | isa = PBXContainerItemProxy; 95 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 96 | proxyType = 2; 97 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 98 | remoteInfo = RCTLinking; 99 | }; 100 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 101 | isa = PBXContainerItemProxy; 102 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 103 | proxyType = 2; 104 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 105 | remoteInfo = RCTText; 106 | }; 107 | CAB1FB7A1DA8000C007EE8BF /* PBXContainerItemProxy */ = { 108 | isa = PBXContainerItemProxy; 109 | containerPortal = CAB1FB751DA8000C007EE8BF /* RCTCameraRoll.xcodeproj */; 110 | proxyType = 2; 111 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 112 | remoteInfo = RCTCameraRoll; 113 | }; 114 | /* End PBXContainerItemProxy section */ 115 | 116 | /* Begin PBXFileReference section */ 117 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; 118 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; 119 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; 120 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 121 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 122 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 123 | 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AwesomeProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 124 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 125 | 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AwesomeProjectTests.m; sourceTree = ""; }; 126 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 127 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 128 | 13B07F961A680F5B00A75B9A /* AwesomeProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AwesomeProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; 129 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AwesomeProject/AppDelegate.h; sourceTree = ""; }; 130 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = AwesomeProject/AppDelegate.m; sourceTree = ""; }; 131 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 132 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AwesomeProject/Images.xcassets; sourceTree = ""; }; 133 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AwesomeProject/Info.plist; sourceTree = ""; }; 134 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AwesomeProject/main.m; sourceTree = ""; }; 135 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 136 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 137 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 138 | CAB1FB751DA8000C007EE8BF /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = "../node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj"; sourceTree = ""; }; 139 | /* End PBXFileReference section */ 140 | 141 | /* Begin PBXFrameworksBuildPhase section */ 142 | 00E356EB1AD99517003FC87E /* Frameworks */ = { 143 | isa = PBXFrameworksBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 151 | isa = PBXFrameworksBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 146834051AC3E58100842450 /* libReact.a in Frameworks */, 155 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 156 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 157 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 158 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, 159 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 160 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 161 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 162 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 163 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 164 | CAB1FB851DA80032007EE8BF /* libRCTCameraRoll.a in Frameworks */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXFrameworksBuildPhase section */ 169 | 170 | /* Begin PBXGroup section */ 171 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 175 | ); 176 | name = Products; 177 | sourceTree = ""; 178 | }; 179 | 00C302B61ABCB90400DB3ED1 /* Products */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 183 | ); 184 | name = Products; 185 | sourceTree = ""; 186 | }; 187 | 00C302BC1ABCB91800DB3ED1 /* Products */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 191 | ); 192 | name = Products; 193 | sourceTree = ""; 194 | }; 195 | 00C302D41ABCB9D200DB3ED1 /* Products */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 199 | ); 200 | name = Products; 201 | sourceTree = ""; 202 | }; 203 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 207 | ); 208 | name = Products; 209 | sourceTree = ""; 210 | }; 211 | 00E356EF1AD99517003FC87E /* AwesomeProjectTests */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */, 215 | 00E356F01AD99517003FC87E /* Supporting Files */, 216 | ); 217 | path = AwesomeProjectTests; 218 | sourceTree = ""; 219 | }; 220 | 00E356F01AD99517003FC87E /* Supporting Files */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | 00E356F11AD99517003FC87E /* Info.plist */, 224 | ); 225 | name = "Supporting Files"; 226 | sourceTree = ""; 227 | }; 228 | 139105B71AF99BAD00B5F7CC /* Products */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 232 | ); 233 | name = Products; 234 | sourceTree = ""; 235 | }; 236 | 139FDEE71B06529A00C62182 /* Products */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 240 | ); 241 | name = Products; 242 | sourceTree = ""; 243 | }; 244 | 13B07FAE1A68108700A75B9A /* AwesomeProject */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 248 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 249 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 250 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 251 | 13B07FB61A68108700A75B9A /* Info.plist */, 252 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 253 | 13B07FB71A68108700A75B9A /* main.m */, 254 | ); 255 | name = AwesomeProject; 256 | sourceTree = ""; 257 | }; 258 | 146834001AC3E56700842450 /* Products */ = { 259 | isa = PBXGroup; 260 | children = ( 261 | 146834041AC3E56700842450 /* libReact.a */, 262 | ); 263 | name = Products; 264 | sourceTree = ""; 265 | }; 266 | 78C398B11ACF4ADC00677621 /* Products */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 270 | ); 271 | name = Products; 272 | sourceTree = ""; 273 | }; 274 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | CAB1FB751DA8000C007EE8BF /* RCTCameraRoll.xcodeproj */, 278 | 146833FF1AC3E56700842450 /* React.xcodeproj */, 279 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 280 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 281 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 282 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 283 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 284 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 285 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 286 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 287 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 288 | ); 289 | name = Libraries; 290 | sourceTree = ""; 291 | }; 292 | 832341B11AAA6A8300B99B32 /* Products */ = { 293 | isa = PBXGroup; 294 | children = ( 295 | 832341B51AAA6A8300B99B32 /* libRCTText.a */, 296 | ); 297 | name = Products; 298 | sourceTree = ""; 299 | }; 300 | 83CBB9F61A601CBA00E9B192 = { 301 | isa = PBXGroup; 302 | children = ( 303 | 13B07FAE1A68108700A75B9A /* AwesomeProject */, 304 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 305 | 00E356EF1AD99517003FC87E /* AwesomeProjectTests */, 306 | 83CBBA001A601CBA00E9B192 /* Products */, 307 | ); 308 | indentWidth = 2; 309 | sourceTree = ""; 310 | tabWidth = 2; 311 | }; 312 | 83CBBA001A601CBA00E9B192 /* Products */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | 13B07F961A680F5B00A75B9A /* AwesomeProject.app */, 316 | 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */, 317 | ); 318 | name = Products; 319 | sourceTree = ""; 320 | }; 321 | CAB1FB761DA8000C007EE8BF /* Products */ = { 322 | isa = PBXGroup; 323 | children = ( 324 | CAB1FB7B1DA8000C007EE8BF /* libRCTCameraRoll.a */, 325 | ); 326 | name = Products; 327 | sourceTree = ""; 328 | }; 329 | /* End PBXGroup section */ 330 | 331 | /* Begin PBXNativeTarget section */ 332 | 00E356ED1AD99517003FC87E /* AwesomeProjectTests */ = { 333 | isa = PBXNativeTarget; 334 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */; 335 | buildPhases = ( 336 | 00E356EA1AD99517003FC87E /* Sources */, 337 | 00E356EB1AD99517003FC87E /* Frameworks */, 338 | 00E356EC1AD99517003FC87E /* Resources */, 339 | ); 340 | buildRules = ( 341 | ); 342 | dependencies = ( 343 | 00E356F51AD99517003FC87E /* PBXTargetDependency */, 344 | ); 345 | name = AwesomeProjectTests; 346 | productName = AwesomeProjectTests; 347 | productReference = 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */; 348 | productType = "com.apple.product-type.bundle.unit-test"; 349 | }; 350 | 13B07F861A680F5B00A75B9A /* AwesomeProject */ = { 351 | isa = PBXNativeTarget; 352 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */; 353 | buildPhases = ( 354 | 13B07F871A680F5B00A75B9A /* Sources */, 355 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 356 | 13B07F8E1A680F5B00A75B9A /* Resources */, 357 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 358 | ); 359 | buildRules = ( 360 | ); 361 | dependencies = ( 362 | ); 363 | name = AwesomeProject; 364 | productName = "Hello World"; 365 | productReference = 13B07F961A680F5B00A75B9A /* AwesomeProject.app */; 366 | productType = "com.apple.product-type.application"; 367 | }; 368 | /* End PBXNativeTarget section */ 369 | 370 | /* Begin PBXProject section */ 371 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 372 | isa = PBXProject; 373 | attributes = { 374 | LastUpgradeCheck = 0610; 375 | ORGANIZATIONNAME = Facebook; 376 | TargetAttributes = { 377 | 00E356ED1AD99517003FC87E = { 378 | CreatedOnToolsVersion = 6.2; 379 | TestTargetID = 13B07F861A680F5B00A75B9A; 380 | }; 381 | }; 382 | }; 383 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */; 384 | compatibilityVersion = "Xcode 3.2"; 385 | developmentRegion = English; 386 | hasScannedForEncodings = 0; 387 | knownRegions = ( 388 | en, 389 | Base, 390 | ); 391 | mainGroup = 83CBB9F61A601CBA00E9B192; 392 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 393 | projectDirPath = ""; 394 | projectReferences = ( 395 | { 396 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; 397 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 398 | }, 399 | { 400 | ProductGroup = CAB1FB761DA8000C007EE8BF /* Products */; 401 | ProjectRef = CAB1FB751DA8000C007EE8BF /* RCTCameraRoll.xcodeproj */; 402 | }, 403 | { 404 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; 405 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 406 | }, 407 | { 408 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; 409 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 410 | }, 411 | { 412 | ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; 413 | ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 414 | }, 415 | { 416 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; 417 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 418 | }, 419 | { 420 | ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; 421 | ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 422 | }, 423 | { 424 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; 425 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 426 | }, 427 | { 428 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; 429 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 430 | }, 431 | { 432 | ProductGroup = 139FDEE71B06529A00C62182 /* Products */; 433 | ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 434 | }, 435 | { 436 | ProductGroup = 146834001AC3E56700842450 /* Products */; 437 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; 438 | }, 439 | ); 440 | projectRoot = ""; 441 | targets = ( 442 | 13B07F861A680F5B00A75B9A /* AwesomeProject */, 443 | 00E356ED1AD99517003FC87E /* AwesomeProjectTests */, 444 | ); 445 | }; 446 | /* End PBXProject section */ 447 | 448 | /* Begin PBXReferenceProxy section */ 449 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 450 | isa = PBXReferenceProxy; 451 | fileType = archive.ar; 452 | path = libRCTActionSheet.a; 453 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; 454 | sourceTree = BUILT_PRODUCTS_DIR; 455 | }; 456 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { 457 | isa = PBXReferenceProxy; 458 | fileType = archive.ar; 459 | path = libRCTGeolocation.a; 460 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; 461 | sourceTree = BUILT_PRODUCTS_DIR; 462 | }; 463 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 464 | isa = PBXReferenceProxy; 465 | fileType = archive.ar; 466 | path = libRCTImage.a; 467 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; 468 | sourceTree = BUILT_PRODUCTS_DIR; 469 | }; 470 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 471 | isa = PBXReferenceProxy; 472 | fileType = archive.ar; 473 | path = libRCTNetwork.a; 474 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; 475 | sourceTree = BUILT_PRODUCTS_DIR; 476 | }; 477 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 478 | isa = PBXReferenceProxy; 479 | fileType = archive.ar; 480 | path = libRCTVibration.a; 481 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; 482 | sourceTree = BUILT_PRODUCTS_DIR; 483 | }; 484 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { 485 | isa = PBXReferenceProxy; 486 | fileType = archive.ar; 487 | path = libRCTSettings.a; 488 | remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; 489 | sourceTree = BUILT_PRODUCTS_DIR; 490 | }; 491 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { 492 | isa = PBXReferenceProxy; 493 | fileType = archive.ar; 494 | path = libRCTWebSocket.a; 495 | remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; 496 | sourceTree = BUILT_PRODUCTS_DIR; 497 | }; 498 | 146834041AC3E56700842450 /* libReact.a */ = { 499 | isa = PBXReferenceProxy; 500 | fileType = archive.ar; 501 | path = libReact.a; 502 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; 503 | sourceTree = BUILT_PRODUCTS_DIR; 504 | }; 505 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { 506 | isa = PBXReferenceProxy; 507 | fileType = archive.ar; 508 | path = libRCTLinking.a; 509 | remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; 510 | sourceTree = BUILT_PRODUCTS_DIR; 511 | }; 512 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 513 | isa = PBXReferenceProxy; 514 | fileType = archive.ar; 515 | path = libRCTText.a; 516 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; 517 | sourceTree = BUILT_PRODUCTS_DIR; 518 | }; 519 | CAB1FB7B1DA8000C007EE8BF /* libRCTCameraRoll.a */ = { 520 | isa = PBXReferenceProxy; 521 | fileType = archive.ar; 522 | path = libRCTCameraRoll.a; 523 | remoteRef = CAB1FB7A1DA8000C007EE8BF /* PBXContainerItemProxy */; 524 | sourceTree = BUILT_PRODUCTS_DIR; 525 | }; 526 | /* End PBXReferenceProxy section */ 527 | 528 | /* Begin PBXResourcesBuildPhase section */ 529 | 00E356EC1AD99517003FC87E /* Resources */ = { 530 | isa = PBXResourcesBuildPhase; 531 | buildActionMask = 2147483647; 532 | files = ( 533 | ); 534 | runOnlyForDeploymentPostprocessing = 0; 535 | }; 536 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 537 | isa = PBXResourcesBuildPhase; 538 | buildActionMask = 2147483647; 539 | files = ( 540 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 541 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 542 | ); 543 | runOnlyForDeploymentPostprocessing = 0; 544 | }; 545 | /* End PBXResourcesBuildPhase section */ 546 | 547 | /* Begin PBXShellScriptBuildPhase section */ 548 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 549 | isa = PBXShellScriptBuildPhase; 550 | buildActionMask = 2147483647; 551 | files = ( 552 | ); 553 | inputPaths = ( 554 | ); 555 | name = "Bundle React Native code and images"; 556 | outputPaths = ( 557 | ); 558 | runOnlyForDeploymentPostprocessing = 0; 559 | shellPath = /bin/sh; 560 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; 561 | }; 562 | /* End PBXShellScriptBuildPhase section */ 563 | 564 | /* Begin PBXSourcesBuildPhase section */ 565 | 00E356EA1AD99517003FC87E /* Sources */ = { 566 | isa = PBXSourcesBuildPhase; 567 | buildActionMask = 2147483647; 568 | files = ( 569 | 00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */, 570 | ); 571 | runOnlyForDeploymentPostprocessing = 0; 572 | }; 573 | 13B07F871A680F5B00A75B9A /* Sources */ = { 574 | isa = PBXSourcesBuildPhase; 575 | buildActionMask = 2147483647; 576 | files = ( 577 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 578 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 579 | ); 580 | runOnlyForDeploymentPostprocessing = 0; 581 | }; 582 | /* End PBXSourcesBuildPhase section */ 583 | 584 | /* Begin PBXTargetDependency section */ 585 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { 586 | isa = PBXTargetDependency; 587 | target = 13B07F861A680F5B00A75B9A /* AwesomeProject */; 588 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; 589 | }; 590 | /* End PBXTargetDependency section */ 591 | 592 | /* Begin PBXVariantGroup section */ 593 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { 594 | isa = PBXVariantGroup; 595 | children = ( 596 | 13B07FB21A68108700A75B9A /* Base */, 597 | ); 598 | name = LaunchScreen.xib; 599 | path = AwesomeProject; 600 | sourceTree = ""; 601 | }; 602 | /* End PBXVariantGroup section */ 603 | 604 | /* Begin XCBuildConfiguration section */ 605 | 00E356F61AD99517003FC87E /* Debug */ = { 606 | isa = XCBuildConfiguration; 607 | buildSettings = { 608 | BUNDLE_LOADER = "$(TEST_HOST)"; 609 | GCC_PREPROCESSOR_DEFINITIONS = ( 610 | "DEBUG=1", 611 | "$(inherited)", 612 | ); 613 | INFOPLIST_FILE = AwesomeProjectTests/Info.plist; 614 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 615 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 616 | PRODUCT_NAME = "$(TARGET_NAME)"; 617 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject"; 618 | }; 619 | name = Debug; 620 | }; 621 | 00E356F71AD99517003FC87E /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | BUNDLE_LOADER = "$(TEST_HOST)"; 625 | COPY_PHASE_STRIP = NO; 626 | INFOPLIST_FILE = AwesomeProjectTests/Info.plist; 627 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 628 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 629 | PRODUCT_NAME = "$(TARGET_NAME)"; 630 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject"; 631 | }; 632 | name = Release; 633 | }; 634 | 13B07F941A680F5B00A75B9A /* Debug */ = { 635 | isa = XCBuildConfiguration; 636 | buildSettings = { 637 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 638 | CURRENT_PROJECT_VERSION = 1; 639 | DEAD_CODE_STRIPPING = NO; 640 | HEADER_SEARCH_PATHS = ( 641 | "$(inherited)", 642 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 643 | "$(SRCROOT)/../node_modules/react-native/React/**", 644 | ); 645 | INFOPLIST_FILE = AwesomeProject/Info.plist; 646 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 647 | OTHER_LDFLAGS = ( 648 | "$(inherited)", 649 | "-ObjC", 650 | "-lc++", 651 | ); 652 | PRODUCT_NAME = AwesomeProject; 653 | VERSIONING_SYSTEM = "apple-generic"; 654 | }; 655 | name = Debug; 656 | }; 657 | 13B07F951A680F5B00A75B9A /* Release */ = { 658 | isa = XCBuildConfiguration; 659 | buildSettings = { 660 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 661 | CURRENT_PROJECT_VERSION = 1; 662 | HEADER_SEARCH_PATHS = ( 663 | "$(inherited)", 664 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 665 | "$(SRCROOT)/../node_modules/react-native/React/**", 666 | ); 667 | INFOPLIST_FILE = AwesomeProject/Info.plist; 668 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 669 | OTHER_LDFLAGS = ( 670 | "$(inherited)", 671 | "-ObjC", 672 | "-lc++", 673 | ); 674 | PRODUCT_NAME = AwesomeProject; 675 | VERSIONING_SYSTEM = "apple-generic"; 676 | }; 677 | name = Release; 678 | }; 679 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 680 | isa = XCBuildConfiguration; 681 | buildSettings = { 682 | ALWAYS_SEARCH_USER_PATHS = NO; 683 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 684 | CLANG_CXX_LIBRARY = "libc++"; 685 | CLANG_ENABLE_MODULES = YES; 686 | CLANG_ENABLE_OBJC_ARC = YES; 687 | CLANG_WARN_BOOL_CONVERSION = YES; 688 | CLANG_WARN_CONSTANT_CONVERSION = YES; 689 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 690 | CLANG_WARN_EMPTY_BODY = YES; 691 | CLANG_WARN_ENUM_CONVERSION = YES; 692 | CLANG_WARN_INT_CONVERSION = YES; 693 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 694 | CLANG_WARN_UNREACHABLE_CODE = YES; 695 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 696 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 697 | COPY_PHASE_STRIP = NO; 698 | ENABLE_STRICT_OBJC_MSGSEND = YES; 699 | GCC_C_LANGUAGE_STANDARD = gnu99; 700 | GCC_DYNAMIC_NO_PIC = NO; 701 | GCC_OPTIMIZATION_LEVEL = 0; 702 | GCC_PREPROCESSOR_DEFINITIONS = ( 703 | "DEBUG=1", 704 | "$(inherited)", 705 | ); 706 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 707 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 708 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 709 | GCC_WARN_UNDECLARED_SELECTOR = YES; 710 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 711 | GCC_WARN_UNUSED_FUNCTION = YES; 712 | GCC_WARN_UNUSED_VARIABLE = YES; 713 | HEADER_SEARCH_PATHS = ( 714 | "$(inherited)", 715 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 716 | "$(SRCROOT)/../node_modules/react-native/React/**", 717 | ); 718 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 719 | MTL_ENABLE_DEBUG_INFO = YES; 720 | ONLY_ACTIVE_ARCH = YES; 721 | SDKROOT = iphoneos; 722 | }; 723 | name = Debug; 724 | }; 725 | 83CBBA211A601CBA00E9B192 /* Release */ = { 726 | isa = XCBuildConfiguration; 727 | buildSettings = { 728 | ALWAYS_SEARCH_USER_PATHS = NO; 729 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 730 | CLANG_CXX_LIBRARY = "libc++"; 731 | CLANG_ENABLE_MODULES = YES; 732 | CLANG_ENABLE_OBJC_ARC = YES; 733 | CLANG_WARN_BOOL_CONVERSION = YES; 734 | CLANG_WARN_CONSTANT_CONVERSION = YES; 735 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 736 | CLANG_WARN_EMPTY_BODY = YES; 737 | CLANG_WARN_ENUM_CONVERSION = YES; 738 | CLANG_WARN_INT_CONVERSION = YES; 739 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 740 | CLANG_WARN_UNREACHABLE_CODE = YES; 741 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 742 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 743 | COPY_PHASE_STRIP = YES; 744 | ENABLE_NS_ASSERTIONS = NO; 745 | ENABLE_STRICT_OBJC_MSGSEND = YES; 746 | GCC_C_LANGUAGE_STANDARD = gnu99; 747 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 748 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 749 | GCC_WARN_UNDECLARED_SELECTOR = YES; 750 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 751 | GCC_WARN_UNUSED_FUNCTION = YES; 752 | GCC_WARN_UNUSED_VARIABLE = YES; 753 | HEADER_SEARCH_PATHS = ( 754 | "$(inherited)", 755 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 756 | "$(SRCROOT)/../node_modules/react-native/React/**", 757 | ); 758 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 759 | MTL_ENABLE_DEBUG_INFO = NO; 760 | SDKROOT = iphoneos; 761 | VALIDATE_PRODUCT = YES; 762 | }; 763 | name = Release; 764 | }; 765 | /* End XCBuildConfiguration section */ 766 | 767 | /* Begin XCConfigurationList section */ 768 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */ = { 769 | isa = XCConfigurationList; 770 | buildConfigurations = ( 771 | 00E356F61AD99517003FC87E /* Debug */, 772 | 00E356F71AD99517003FC87E /* Release */, 773 | ); 774 | defaultConfigurationIsVisible = 0; 775 | defaultConfigurationName = Release; 776 | }; 777 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */ = { 778 | isa = XCConfigurationList; 779 | buildConfigurations = ( 780 | 13B07F941A680F5B00A75B9A /* Debug */, 781 | 13B07F951A680F5B00A75B9A /* Release */, 782 | ); 783 | defaultConfigurationIsVisible = 0; 784 | defaultConfigurationName = Release; 785 | }; 786 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */ = { 787 | isa = XCConfigurationList; 788 | buildConfigurations = ( 789 | 83CBBA201A601CBA00E9B192 /* Debug */, 790 | 83CBBA211A601CBA00E9B192 /* Release */, 791 | ); 792 | defaultConfigurationIsVisible = 0; 793 | defaultConfigurationName = Release; 794 | }; 795 | /* End XCConfigurationList section */ 796 | }; 797 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 798 | } 799 | -------------------------------------------------------------------------------- /ios/AwesomeProject.xcodeproj/xcshareddata/xcschemes/AwesomeProject.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /ios/AwesomeProject/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/AwesomeProject/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTBundleURLProvider.h" 13 | #import "RCTRootView.h" 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"AwesomeProject" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ios/AwesomeProject/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/AwesomeProject/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /ios/AwesomeProject/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 | NSPhotoLibraryUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | 46 | NSExceptionDomains 47 | 48 | localhost 49 | 50 | NSTemporaryExceptionAllowsInsecureHTTPLoads 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/AwesomeProject/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/AwesomeProjectTests/AwesomeProjectTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTLog.h" 14 | #import "RCTRootView.h" 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface AwesomeProjectTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation AwesomeProjectTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /ios/AwesomeProjectTests/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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AwesomeProject", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start" 7 | }, 8 | "dependencies": { 9 | "emoji-datasource": "^2.4.4", 10 | "react": "15.3.2", 11 | "react-native": "0.34.1", 12 | "react-native-simple-onboarding": "~0.1.0", 13 | "react-redux": "^4.4.5", 14 | "redux": "^3.6.0" 15 | }, 16 | "devDependencies": { 17 | "flow-bin": "^0.32.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { 5 | StyleSheet, 6 | View, 7 | ScrollView, 8 | KeyboardAvoidingView, 9 | NavigationExperimental, 10 | } from 'react-native'; 11 | import { connect } from 'react-redux'; 12 | import Onboarding from 'react-native-simple-onboarding'; 13 | 14 | import { navPop, navPush } from './store'; 15 | import { getChatById, getNavigationState } from './selectors'; 16 | 17 | import Header from './common/Header'; 18 | import ChatsViewScene from './scene/Chats'; 19 | import ChatViewScene from './scene/Chat'; 20 | 21 | const { 22 | CardStack: NavigationCardStack, 23 | } = NavigationExperimental; 24 | 25 | function mapStateToProps(state) { 26 | return { 27 | navigationState: getNavigationState(state), 28 | getChatById: getChatById.bind(null, state), 29 | }; 30 | } 31 | 32 | const mapDispatchToProps = { 33 | navPush, 34 | navPop, 35 | }; 36 | 37 | class AwesomeProject extends Component { 38 | props: { 39 | navigationState: Object, 40 | getChatById: (chatId: number) => Object, 41 | navPush: (chatId: number) => void, 42 | navPop: () => void, 43 | }; 44 | 45 | constructor() { 46 | super(); 47 | this.state = { 48 | completedOnboarding: false, 49 | }; 50 | } 51 | 52 | render() { 53 | const Circle = () => ( 54 | 55 | ); 56 | const Square = () => ( 57 | 58 | ); 59 | 60 | if (!this.state.completedOnboarding) { 61 | return ( 62 | , title: 'Simple Messenger UI', subtitle: 'Implemented in React Native' }, 65 | { backgroundColor: "#fe6e58", image: , title: 'Welcome', subtitle: 'To Earth' }, 66 | { backgroundColor: "#999", image: , title: 'Also', subtitle: 'Mars is nice' }, 67 | ]} 68 | onEnd={() => this.setState({ completedOnboarding: true })} 69 | /> 70 | ); 71 | } 72 | return ( 73 | this.props.navPop()} 75 | navigationState={(this.props.navigationState : any)} 76 | renderScene={this._renderScene} 77 | renderHeader={null} 78 | style={{ flex: 1 }} 79 | /> 80 | ); 81 | } 82 | 83 | _renderScene = (info) => { 84 | const { route } = info.scene; 85 | const { chatId } = (route : any); 86 | 87 | const chat = chatId ? this.props.getChatById(chatId) : null; 88 | const title = chat ? chat.name : 'Chats'; 89 | const chatView = ; 90 | const chatsView = ; 91 | 92 | return ( 93 | 94 | 95 |
100 | {chatId ? chatView : chatsView} 101 | 102 | 103 | ); 104 | }; 105 | } 106 | 107 | const styles = StyleSheet.create({ 108 | container: { 109 | flex: 1, 110 | backgroundColor: 'white', 111 | }, 112 | }); 113 | const ConnectedAwesomeProject = connect(mapStateToProps, mapDispatchToProps)(AwesomeProject); 114 | 115 | export default ConnectedAwesomeProject; 116 | -------------------------------------------------------------------------------- /src/colors.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | blue: 'rgba(0, 149, 255, 0.95)', 3 | }; 4 | 5 | export default colors; 6 | -------------------------------------------------------------------------------- /src/common/Avatar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Image } from 'react-native'; 5 | 6 | const Avatar = ({ username, size, style } : { username: string, size: number, style?: Object }) => ( 7 | 11 | ); 12 | 13 | export default Avatar; 14 | -------------------------------------------------------------------------------- /src/common/BackButton.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Text, TouchableOpacity } from 'react-native'; 5 | 6 | import colors from '../colors'; 7 | 8 | const styles = { 9 | backButton: { 10 | color: "#333", 11 | fontSize: 44, 12 | lineHeight: 44, 13 | color: colors.blue, 14 | }, 15 | }; 16 | 17 | const BackButton = ({ onPress } : { onPress: () => void }) => ( 18 | 19 | 20 | 21 | ); 22 | 23 | export default BackButton; 24 | -------------------------------------------------------------------------------- /src/common/Badge.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, Text } from 'react-native'; 5 | 6 | import colors from '../colors'; 7 | 8 | const Badge = ({ count, style, size } : { count: number, size: number, style?: Object }) => ( 9 | 10 | {count} 11 | 12 | ); 13 | 14 | export default Badge; 15 | -------------------------------------------------------------------------------- /src/common/EmojiPicker.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, ScrollView, Text, TouchableOpacity } from 'react-native'; 5 | 6 | import emojiData from 'emoji-datasource'; 7 | const CATEGORIES = ['People', 'Nature', 'Foods', 'Activity', 'Places', 'Objects', 'Symbols', 'Flags']; 8 | 9 | const charFromCode = utf16 => String.fromCodePoint(...utf16.split('-').map(u => '0x' + u)); 10 | 11 | const _emoji = emojiData.reduce((acc, item) => { 12 | const { category } = item; 13 | if (!acc[category]) acc[category] = []; 14 | acc[category].push({ sortOrder: item.sort_order, char: charFromCode(item.unified) }); 15 | return acc; 16 | }, {}); 17 | 18 | const emoji = CATEGORIES.map(category => { 19 | let items = _emoji[category] || []; 20 | items = items.sort((a, b) => { 21 | return (a.sortOrder > b.sortOrder) ? 1 : -1; 22 | }).map(x => x.char); 23 | return { category, items }; 24 | }); 25 | 26 | const EMOJI_SIZE = 40; 27 | const EmojiItem = ({ item, onPress } : { item: string, onPress: () => void }) => ( 28 | 29 | 30 | 31 | {item} 32 | 33 | 34 | 35 | ); 36 | 37 | const EmojiCategory = ({ name, items, onPick } : { name: string, items: Array, onPick: (emoji: string) => void }) => ( 38 | 39 | {name} 40 | 41 | {items.map((em, idx) => ( 42 | onPick(em)} item={em} /> 43 | ))} 44 | 45 | 46 | ); 47 | 48 | const EmojiPicker = ({ onPick } : { onPick: (emoji: string) => void }) => ( 49 | 50 | 51 | {emoji.map((category, idx) => ( 52 | 53 | ))} 54 | 55 | 56 | ); 57 | 58 | const styles = { 59 | picker: { 60 | flex: 0, 61 | height: 240, 62 | backgroundColor: 'rgba(0, 0, 0, 0.1)', 63 | borderTopWidth: 1, 64 | borderTopColor: "#ddd", 65 | }, 66 | category: { 67 | flex: 0, 68 | paddingHorizontal: 14, 69 | paddingTop: 2, 70 | }, 71 | categoryName: { 72 | paddingVertical: 8, 73 | paddingHorizontal: 4, 74 | fontSize: 12, 75 | color: "#888", 76 | }, 77 | categoryItems: { 78 | flex: 1, 79 | flexWrap: 'wrap', 80 | }, 81 | }; 82 | 83 | export default EmojiPicker; 84 | -------------------------------------------------------------------------------- /src/common/Header.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, Text } from 'react-native'; 5 | 6 | import colors from '../colors'; 7 | import Navbar from './Navbar'; 8 | import BackButton from './BackButton'; 9 | 10 | const styles = { 11 | container: { 12 | backgroundColor: 'rgba(0, 0, 0, 0.025)', 13 | borderBottomWidth: 1, 14 | borderBottomColor: 'rgba(0, 0, 0, 0.075)', 15 | }, 16 | title: { 17 | flex: 0, 18 | textAlign: 'center', 19 | fontSize: 16, 20 | lineHeight: 16, 21 | color: "#333", 22 | fontWeight: '600', 23 | }, 24 | titleWithSub: { 25 | color: "#222", 26 | fontSize: 14, 27 | lineHeight: 14, 28 | fontWeight: '500', 29 | }, 30 | subtitle: { 31 | flex: 0, 32 | textAlign: 'center', 33 | fontSize: 12, 34 | lineHeight: 12, 35 | color: "#888", 36 | }, 37 | titleContainer: { 38 | flex: 1, 39 | flexDirection: 'column', 40 | paddingVertical: 8, 41 | height: 44, 42 | }, 43 | }; 44 | 45 | const StatusBarPadding = () => ; 46 | 47 | const Title = ({ title, subtitle } : { title: string, subtitle?: ?string }) => ( 48 | 49 | 50 | {title} 51 | 52 | {subtitle ? ( 53 | 54 | {subtitle} 55 | 56 | ) : null} 57 | 58 | ); 59 | 60 | const Header = ({ onBack, title, subtitle } : { onBack?: ?() => void, title: string, subtitle?: ?string }) => ( 61 | 62 | 63 | : null} 65 | > 66 | 67 | </Navbar> 68 | </View> 69 | ); 70 | 71 | export default Header; 72 | -------------------------------------------------------------------------------- /src/common/IconButton.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, Text, TouchableOpacity } from 'react-native'; 5 | 6 | import colors from '../colors'; 7 | 8 | const Button = ({ size, onPress, children } : { size: number, onPress: () => void, children?: ?any }) => ( 9 | <View style={{ width: size, height: size }}> 10 | <TouchableOpacity style={{ width: size, height: size }} onPress={onPress}> 11 | <View style={{ width: size, height: size, backgroundColor: 'white', justifyContent: 'center', alignItems: 'center' }}> 12 | <Text style={{ flex: 0, color: colors.blue, fontSize: size / 1.7, lineHeight: size / 1.7, textAlign: 'center' }}>{children}</Text> 13 | </View> 14 | </TouchableOpacity> 15 | </View> 16 | ); 17 | 18 | export default Button; 19 | -------------------------------------------------------------------------------- /src/common/Navbar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, Text, TouchableOpacity } from 'react-native'; 5 | 6 | const styles = { 7 | navbar: { 8 | paddingHorizontal: 12, 9 | flexDirection: 'row', 10 | alignItems: 'center', 11 | }, 12 | }; 13 | const Navbar = ({ leftContent, children } : { leftContent: any, children?: ?any }) => ( 14 | <View style={styles.navbar}> 15 | {leftContent} 16 | {children} 17 | {leftContent ? <View pointerEvents="none" style={{ opacity: 0 }}>{leftContent}</View> : null} 18 | </View> 19 | ); 20 | 21 | export default Navbar; 22 | -------------------------------------------------------------------------------- /src/scene/Chat/BigEmoji.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Text } from 'react-native'; 5 | 6 | const BigEmoji = ({ text, style } : { text: string, style?: ?Object }) => ( 7 | <Text style={{ ...styles.emoji, ...style }}> 8 | {text} 9 | </Text> 10 | ); 11 | 12 | const styles = { 13 | emoji: { 14 | fontSize: 50, 15 | lineHeight: 50, 16 | paddingBottom: 3, 17 | backgroundColor: 'rgba(0, 0, 0, 0)', 18 | padding: 0, 19 | }, 20 | }; 21 | 22 | export default BigEmoji; 23 | -------------------------------------------------------------------------------- /src/scene/Chat/MessageBubble.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Text, View } from 'react-native'; 5 | 6 | import colors from '../../colors'; 7 | 8 | const styles = { 9 | textView: { 10 | borderRadius: 20, 11 | backgroundColor: 'rgba(0, 0, 0, 0.1)', 12 | }, 13 | ownTextView: { 14 | backgroundColor: colors.blue, 15 | }, 16 | text: { 17 | flex: 1, 18 | paddingVertical: 10, 19 | paddingHorizontal: 15, 20 | fontSize: 18, 21 | color: '#333', 22 | textAlign: 'left', 23 | }, 24 | ownText: { 25 | color: '#fff', 26 | textAlign: 'right', 27 | }, 28 | }; 29 | 30 | const MessageBubble = ({ own, text, style } : { own: bool, text: string, style?: ?Object }) => { 31 | const textStyle = {}; 32 | const viewStyle = Object.assign({}, styles.textView); 33 | if (own) { 34 | Object.assign(textStyle, styles.ownText); 35 | Object.assign(viewStyle, styles.ownTextView); 36 | } 37 | return ( 38 | <View style={{ ...viewStyle }}> 39 | <Text style={{ ...styles.text, ...textStyle }}>{text}</Text> 40 | </View> 41 | ); 42 | }; 43 | 44 | export default MessageBubble; 45 | -------------------------------------------------------------------------------- /src/scene/Chat/MessageRow.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { 5 | Text, 6 | View, 7 | Image, 8 | TouchableOpacity, 9 | MapView, 10 | } from 'react-native'; 11 | 12 | import { isEmoji } from '../../util'; 13 | import Avatar from '../../common/Avatar'; 14 | 15 | import MessageBubble from './MessageBubble'; 16 | import BigEmoji from './BigEmoji'; 17 | 18 | const styles = { 19 | container: { 20 | flexDirection: 'row', 21 | padding: 10, 22 | }, 23 | pad: { 24 | width: 40, 25 | height: 40, 26 | }, 27 | }; 28 | 29 | const ContentObject = ({ own, type, text, coords, uri }) => { 30 | let content; 31 | if (type === 'text') { 32 | const emoji = isEmoji(text.trim()); 33 | if (emoji) { 34 | content = ( 35 | <BigEmoji text={text} style={{ textAlign: own ? 'right' : 'left' }} /> 36 | ); 37 | } else { 38 | content = ( 39 | <MessageBubble own={own} text={text} /> 40 | ); 41 | } 42 | } else if (type === 'location') { 43 | content = ( 44 | <MapView style={{ height: 150, flex: 2 }} annotations={[coords]} region={coords} /> 45 | ); 46 | } else if (type === 'image') { 47 | content = ( 48 | <Image source={{ uri }} style={{ height: 150, flex: 2 }} /> 49 | ); 50 | } 51 | return content; 52 | }; 53 | 54 | const MessageRow = ({ username, own, ...msg } : { username: string, own: bool }) => { 55 | const content = <ContentObject own={own} {...msg} />; 56 | return own ? ( 57 | <View style={styles.container}> 58 | <View username={username} style={{ ...styles.pad, marginRight: 10 }} /> 59 | <View style={{ flex: 1 }} /> 60 | {content} 61 | </View> 62 | ) : ( 63 | <View style={styles.container}> 64 | <Avatar size={40} username={username} style={{ marginRight: 10 }} /> 65 | {content} 66 | <View style={{ flex: 1 }} /> 67 | <View style={{ ...styles.pad, marginLeft: 10 }} /> 68 | </View> 69 | ); 70 | }; 71 | export default MessageRow; 72 | -------------------------------------------------------------------------------- /src/scene/Chat/Toolbar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { Text, View, TextInput } from 'react-native'; 5 | 6 | import colors from '../../colors'; 7 | import Button from '../../common/IconButton'; 8 | 9 | const Toolbar = ( 10 | { text, onShowActions, onToggleEmoji, onSendMessage, onChangeText, onKeyboardEvent } : 11 | { text: string, onShowActions: () => void, onToggleEmoji: () => void, onSendMessage: () => void, onChangeText: (text: string) => void, onKeyboardEvent: (shown: bool) => void } 12 | ) => ( 13 | <View style={styles.container}> 14 | <Button size={50} onPress={onShowActions}>+</Button> 15 | <TextInput 16 | style={styles.input} 17 | selectionColor={colors.blue} 18 | placeholder="Type here..." 19 | value={text} 20 | blurOnSubmit={false} 21 | returnKeyType="send" 22 | onChangeText={onChangeText} 23 | onSubmitEditing={onSendMessage} 24 | onFocus={() => onKeyboardEvent(true)} 25 | onBlur={() => onKeyboardEvent(false)} 26 | /> 27 | <Button size={50} onPress={onToggleEmoji}>☺︎</Button> 28 | <Button size={50} onPress={onSendMessage}>→</Button> 29 | </View> 30 | ); 31 | 32 | const styles = { 33 | container: { 34 | flexDirection: 'row', 35 | height: 50, 36 | backgroundColor: 'white', 37 | borderTopWidth: 1, 38 | borderTopColor: "#ddd", 39 | }, 40 | input: { 41 | flex: 1, 42 | padding: 10, 43 | height: 50, 44 | fontSize: 16, 45 | lineHeight: 24, 46 | color: '#666', 47 | }, 48 | }; 49 | 50 | export default Toolbar; 51 | -------------------------------------------------------------------------------- /src/scene/Chat/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { 5 | ActionSheetIOS, 6 | ImagePickerIOS, 7 | View, 8 | ScrollView, 9 | } from 'react-native'; 10 | import dismissKeyboard from 'dismissKeyboard'; 11 | import { connect } from 'react-redux'; 12 | 13 | import { setText, sendTextMessage, sendImage, sendLocation } from '../../store'; 14 | import { getName, getChatById, getMessages, getText } from '../../selectors'; 15 | import type { Message, Coords } from '../../store'; 16 | import EmojiPicker from '../../common/EmojiPicker'; 17 | 18 | import MessageRow from './MessageRow'; 19 | import Toolbar from './Toolbar'; 20 | 21 | function showActionSheet(actions : Array<{ label: string, fn?: ?() => void }>) { 22 | ActionSheetIOS.showActionSheetWithOptions({ 23 | options: actions.map(a => a.label), 24 | cancelButtonIndex: actions.length - 1, 25 | }, buttonIndex => { 26 | const action = actions[buttonIndex]; 27 | if (action && action.fn) action.fn(); 28 | }); 29 | } 30 | 31 | class ChatViewScene extends Component { 32 | state: { 33 | keyboardShown: bool, 34 | emojiShown: bool, 35 | }; 36 | 37 | props: { 38 | chatId: number, 39 | ownUsername: string, 40 | oppUsername: string, 41 | messages: Array<Message>, 42 | text: string, 43 | onSendMessage: (chatId: number, text: string) => void, 44 | onSendImage: (chatId: number, uri: string) => void, 45 | onSendLocation: (chatId: number, coords: Coords) => void, 46 | onChangeText: (text: string) => void, 47 | }; 48 | 49 | constructor() { 50 | super(); 51 | this.state = { 52 | keyboardShown: false, 53 | emojiShown: false, 54 | }; 55 | } 56 | 57 | resetText = () => { 58 | this.props.onChangeText(''); 59 | }; 60 | 61 | sendMessage = () => { 62 | const text = this.props.text; 63 | if (text.length === 0) return; 64 | this.props.onSendMessage(this.props.chatId, text); 65 | this.resetText(); 66 | }; 67 | 68 | promptImage = () => { 69 | ImagePickerIOS.openSelectDialog({}, imageUri => { 70 | this.props.onSendImage(this.props.chatId, imageUri); 71 | }, () => {}); 72 | } 73 | 74 | promptLocation = () => { 75 | navigator.geolocation.getCurrentPosition(position => { 76 | this.props.onSendLocation(this.props.chatId, position.coords); 77 | }); 78 | } 79 | 80 | showActions = () => { 81 | const actions = [ 82 | { 83 | label: 'Photo', 84 | fn: this.promptImage, 85 | }, 86 | { 87 | label: 'Location', 88 | fn: this.promptLocation, 89 | }, 90 | { 91 | label: 'Cancel', 92 | } 93 | ]; 94 | 95 | showActionSheet(actions); 96 | }; 97 | 98 | toggleEmoji = () => { 99 | if (!this.state.emojiShown) { 100 | dismissKeyboard(); 101 | } 102 | this.setState({ emojiShown: !this.state.emojiShown }); 103 | }; 104 | 105 | insertEmoji = (emoji) => { 106 | this.props.onChangeText(this.props.text + emoji); 107 | }; 108 | 109 | render() { 110 | const { ownUsername, oppUsername, messages, text } = this.props; 111 | const { keyboardShown, emojiShown } = this.state; 112 | 113 | return ( 114 | <View style={{ flex: 1 }}> 115 | <ScrollView 116 | style={{ flex: 1, backgroundColor: 'white' }} 117 | contentContainerStyle={{ alignItems: 'stretch' }} 118 | > 119 | <View style={{ height: 20 }} /> 120 | {messages.map((msg, idx) => ( 121 | <MessageRow key={idx} username={msg.own ? ownUsername : oppUsername} {...msg} /> 122 | ))} 123 | </ScrollView> 124 | <Toolbar 125 | text={text} 126 | onShowActions={this.showActions} 127 | onToggleEmoji={this.toggleEmoji} 128 | onSendMessage={this.sendMessage} 129 | onChangeText={this.props.onChangeText} 130 | onKeyboardEvent={shown => this.setState({ keyboardShown: shown })} 131 | /> 132 | {emojiShown ? <EmojiPicker onPick={this.insertEmoji} /> : null} 133 | </View> 134 | ); 135 | } 136 | }; 137 | 138 | function mapStateToProps(state, ownProps) { 139 | const chat = getChatById(state, ownProps.chatId); 140 | if (!chat) return {}; 141 | return { 142 | ownUsername: getName(state), 143 | oppusername: chat.name, 144 | messages: chat.messages, 145 | text: getText(state), 146 | }; 147 | } 148 | 149 | const mapDispatchToProps = { 150 | onChangeText: setText, 151 | onSendMessage: sendTextMessage, 152 | onSendLocation: sendLocation, 153 | onSendImage: sendImage, 154 | }; 155 | 156 | export default connect(mapStateToProps, mapDispatchToProps)(ChatViewScene); 157 | -------------------------------------------------------------------------------- /src/scene/Chats/ChatRow.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, TouchableHighlight, Text } from 'react-native'; 5 | 6 | import Avatar from '../../common/Avatar'; 7 | import Badge from '../../common/Badge'; 8 | 9 | const ChatRow = ({ onPress, name, lastText, unreadCount } : { onPress: () => void, name: string, lastText: ?string, unreadCount: number }) => ( 10 | <TouchableHighlight style={{ flex: 1 }} onPress={onPress}> 11 | <View style={styles.container}> 12 | <Avatar size={50} username={name} style={{ marginRight: 15 }} /> 13 | <View style={{ flex: 1 }}> 14 | <Text style={{ flex: 1, ...styles.title }}>{name}</Text> 15 | <Text style={{ flex: 1, ...styles.subtitle }}>{lastText}</Text> 16 | </View> 17 | {unreadCount > 0 ? <Badge size={28} count={unreadCount} /> : null} 18 | </View> 19 | </TouchableHighlight> 20 | ); 21 | 22 | const styles = { 23 | container: { 24 | flex: 1, 25 | flexDirection: 'row', 26 | alignItems: 'center', 27 | backgroundColor: "#fff", 28 | paddingHorizontal: 20, 29 | paddingVertical: 10, 30 | borderBottomWidth: 1, 31 | borderBottomColor: "#ddd", 32 | }, 33 | title: { 34 | color: "#333", 35 | fontSize: 16, 36 | lineHeight: 22, 37 | marginBottom: 5, 38 | }, 39 | subtitle: { 40 | color: "#888", 41 | fontSize: 14, 42 | lineHeight: 18, 43 | }, 44 | }; 45 | 46 | export default ChatRow; 47 | -------------------------------------------------------------------------------- /src/scene/Chats/SearchBar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { View, TextInput } from 'react-native'; 5 | 6 | const SearchBar = ({ query, onChange } : { query: string, onChange: (text: string) => void }) => ( 7 | <View style={styles.bar}> 8 | <TextInput 9 | style={styles.input} 10 | placeholder="Type to search..." 11 | placeholderTextColor="#999" 12 | clearButtonMode="always" 13 | value={query} 14 | onChangeText={onChange} 15 | /> 16 | </View> 17 | ); 18 | 19 | const styles = { 20 | bar: { 21 | backgroundColor: 'rgba(0, 0, 0, 0.15)', 22 | padding: 10, 23 | }, 24 | input: { 25 | borderRadius: 5, 26 | backgroundColor: 'rgba(255, 255, 255, 0.85)', 27 | height: 30, 28 | fontSize: 14, 29 | paddingVertical: 5, 30 | paddingHorizontal: 10, 31 | textAlign: 'center', 32 | } 33 | }; 34 | 35 | export default SearchBar; 36 | -------------------------------------------------------------------------------- /src/scene/Chats/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { View, ScrollView, TouchableHighlight, Text } from 'react-native'; 6 | import { TextInput } from 'react-native'; 7 | 8 | import { navPush, setQuery } from '../../store'; 9 | import { getFilteredChats, getQuery } from '../../selectors'; 10 | 11 | import ChatRow from './ChatRow'; 12 | import SearchBar from './SearchBar'; 13 | 14 | const ChatsViewScene = ({ query, chats, onSelectChat, onChangeQuery }) => ( 15 | <View style={{ flex: 1 }}> 16 | <ScrollView 17 | style={{ flex: 1 }} 18 | contentContainerStyle={{ alignItems: 'stretch' }} 19 | > 20 | <SearchBar query={query} onChange={onChangeQuery} /> 21 | <View style={{ flex: 1 }}> 22 | {chats.map(chat => ( 23 | <ChatRow 24 | key={chat.id} 25 | name={chat.name} 26 | lastText={chat.lastText} 27 | unreadCount={chat.unreadCount} 28 | onPress={() => onSelectChat(chat.id)} 29 | /> 30 | ))} 31 | </View> 32 | {chats.length !== 0 ? null : ( 33 | <View style={{ padding: 40 }}> 34 | <Text style={{ fontSize: 16, color: "#888", textAlign: 'center' }}> 35 | No chats. 36 | </Text> 37 | </View> 38 | )} 39 | </ScrollView> 40 | </View> 41 | ); 42 | 43 | function mapStateToProps(state) { 44 | const chats = getFilteredChats(state); 45 | const query = getQuery(state); 46 | 47 | return { 48 | chats, 49 | query, 50 | }; 51 | } 52 | 53 | const mapDispatchToProps = { onSelectChat: navPush, onChangeQuery: setQuery }; 54 | 55 | export default connect(mapStateToProps, mapDispatchToProps)(ChatsViewScene); 56 | -------------------------------------------------------------------------------- /src/selectors.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { FullState } from './store'; 4 | 5 | export const getQuery = (state: FullState) => state.main.query; 6 | export const getName = (state: FullState) => state.main.name; 7 | export const getChats = (state : FullState) => state.main.chats; 8 | export const getChatById = (state : FullState, id : number) => { 9 | return getChats(state).find(chat => chat.id === id); 10 | }; 11 | export const getChat = (state : FullState) => { 12 | const currentChatId = getCurrentChatId(state); 13 | if (!currentChatId) return; 14 | return getChatById(state, currentChatId); 15 | }; 16 | export const getMessages = (state : FullState) => { 17 | return (getChat(state) || { messages: [] }).messages; 18 | }; 19 | export const getText = (state : FullState) => state.main.text; 20 | export const getNavigationState = (state : FullState) => state.nav; 21 | export const getCurrentChatId = (state : FullState) : ?number => { 22 | return state.nav.routes.slice(-1)[0].chatId; 23 | }; 24 | 25 | export function getFilteredChats(state : FullState) { 26 | const chats = getChats(state); 27 | const query = getQuery(state); 28 | 29 | const queryRe = new RegExp(query.trim(), 'i'); 30 | const matchesQuery = text => queryRe.exec(text); 31 | 32 | const filteredChats = query.length === 0 ? 33 | chats : 34 | chats.filter(chat => { 35 | if (matchesQuery(chat.name)) return true; 36 | return chat.messages.some(msg => { 37 | if (msg.type !== 'text') return false; 38 | return matchesQuery(msg.text); 39 | }); 40 | }); 41 | 42 | return filteredChats; 43 | } 44 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { combineReducers } from 'redux'; 4 | import { NavigationExperimental } from 'react-native'; 5 | const { 6 | StateUtils: NavigationStateUtils, 7 | } = NavigationExperimental; 8 | 9 | export type Coords = {}; 10 | type BaseNavigationState = { 11 | index: number, 12 | routes: Array<{ 13 | key: string, 14 | title?: string, 15 | }>, 16 | }; 17 | export type RouteInfo = { 18 | key: string, 19 | chatId?: number, 20 | title?: string, 21 | }; 22 | type NavigationState = { 23 | index: number, 24 | routes: Array<RouteInfo>, 25 | }; 26 | 27 | type MessageMeta = { own: bool }; 28 | type TextMessage = MessageMeta & { 29 | type: 'text', 30 | text: string, 31 | }; 32 | type LocationMessage = MessageMeta & { 33 | type: 'location', 34 | coords: Coords, 35 | }; 36 | type ImageMessage = MessageMeta & { 37 | type: 'image', 38 | uri: string, 39 | }; 40 | export type Message = TextMessage | LocationMessage | ImageMessage; 41 | 42 | type State = { 43 | name: string, 44 | text: string, 45 | query: string, 46 | chats: Array<{ 47 | id: number, 48 | name: string, 49 | unreadCount: number, 50 | messages: Array<Message>, 51 | }>, 52 | }; 53 | 54 | export type FullState = { 55 | main: State, 56 | nav: NavigationState, 57 | }; 58 | 59 | const initialNavigationState : NavigationState = { 60 | index: 0, // Starts with first route focused. 61 | routes: [{key: 'Chats'}], // Starts with only one route. 62 | }; 63 | 64 | const initialState : State = { 65 | name: 'Gosha', 66 | text: '', 67 | query: '', 68 | chats: [ 69 | { 70 | id: 1, 71 | name: 'Alice', 72 | unreadCount: 5, 73 | lastText: 'Hey!', 74 | messages: [ 75 | { type: 'text', own: false, text: 'Hey there' }, 76 | { type: 'text', own: false, text: 'Suuuup?' }, 77 | { type: 'text', own: true, text: 'Yo!' }, 78 | ], 79 | }, 80 | { 81 | id: 2, 82 | name: 'Bob', 83 | unreadCount: 1, 84 | lastText: 'Suuuup!', 85 | messages: [ 86 | { type: 'text', own: false, text: 'Suuuup?' }, 87 | ], 88 | }, 89 | { 90 | id: 3, 91 | name: 'Susanne', 92 | unreadCount: 0, 93 | lastText: 'Hi', 94 | messages: [ 95 | { type: 'text', own: false, text: 'Hi' }, 96 | ], 97 | }, 98 | ] 99 | }; 100 | 101 | type SetQuery = { type: 'SET_QUERY', text: string }; 102 | type SetTextAction = { type: 'SET_TEXT', text: string }; 103 | type SendTextAction = { type: 'SEND_TEXT_MESSAGE', chatId: number, text: string }; 104 | type SendImageAction = { type: 'SEND_IMAGE', chatId: number, uri: string }; 105 | type SendLocationAction = { type: 'SEND_LOCATION', chatId: number, coords: Coords }; 106 | type NavPopAction = { type: 'NAV_POP' }; 107 | type NavPushAction = { type: 'NAV_PUSH', chatId: number }; 108 | 109 | type Action = SetQuery | SetTextAction | SendTextAction | SendImageAction | SendLocationAction | any; 110 | 111 | const addMessage = (state: State, currentChatId: number, msg: Message) => { 112 | const { chats } = state; 113 | const newChats = chats.map(chat => { 114 | if (chat.id !== currentChatId) return chat; 115 | return { ...chat, messages: [...chat.messages, msg] }; 116 | }); 117 | return { ...state, chats: newChats }; 118 | }; 119 | const setNavigationState = (state: State, navigationState: NavigationState) => ({ ...state, navigationState }); 120 | 121 | const navReducer = (state : NavigationState = initialNavigationState, action : NavPopAction | NavPushAction | any) : NavigationState => { 122 | switch (action.type) { 123 | case 'NAV_POP': 124 | return (NavigationStateUtils.pop((state: any)) : any); 125 | case 'NAV_PUSH': 126 | return (NavigationStateUtils.push((state: any), { key: 'Chat-' + action.chatId, chatId: action.chatId }) : any ); 127 | default: return state; 128 | } 129 | }; 130 | 131 | const mainReducer = (state : State = initialState, action : Action = {}) : State => { 132 | switch (action.type) { 133 | case 'SET_QUERY': 134 | return { ...state, query: action.text }; 135 | case 'SET_TEXT': 136 | return { ...state, text: action.text }; 137 | case 'SEND_TEXT_MESSAGE': 138 | return addMessage(state, action.chatId, { type: 'text', own: true, text: action.text }); 139 | case 'SEND_IMAGE': 140 | return addMessage(state, action.chatId, { type: 'image', own: true, uri: action.uri }); 141 | case 'SEND_LOCATION': 142 | return addMessage(state, action.chatId, { type: 'location', own: true, coords: action.coords }); 143 | default: return state; 144 | } 145 | }; 146 | 147 | export const reducer = combineReducers({ main: mainReducer, nav: navReducer }); 148 | 149 | export const setQuery = (text: string) : SetQuery => ({ type: 'SET_QUERY', text }); 150 | export const setText = (text: string) : SetTextAction => ({ type: 'SET_TEXT', text }); 151 | export const sendTextMessage = (chatId: number, text: string) : SendTextAction => ({ type: 'SEND_TEXT_MESSAGE', chatId, text }); 152 | export const sendImage = (chatId: number, uri: string) : SendImageAction => ({ type: 'SEND_IMAGE', chatId, uri }); 153 | export const sendLocation = (chatId: number, coords: Coords) : SendLocationAction => ({ type: 'SEND_LOCATION', chatId, coords }); 154 | export const navPop = () : NavPopAction => ({ type: 'NAV_POP' }); 155 | export const navPush = (chatId: number) : NavPushAction => ({ type: 'NAV_PUSH', chatId }); 156 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | export function isEmoji(str) { 2 | return str.match(/^([\uD800-\uDBFF][\uDC00-\uDFFF])+$/g); 3 | } 4 | --------------------------------------------------------------------------------