├── .babelrc ├── .buckconfig ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── README.md ├── app.json ├── index.ios.js ├── ios ├── Launch Screen.storyboard ├── LaunchScreen.xib ├── hnews.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── hnews-tvOS.xcscheme │ │ └── hnews.xcscheme └── hnews │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Info.plist │ ├── LaunchImage.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ └── Icon-App-60x60@3x.png │ └── Contents.json │ └── main.m ├── js ├── app.container.js ├── components │ ├── generic │ │ ├── button.js │ │ ├── error.js │ │ ├── link.js │ │ ├── list.js │ │ ├── loader.js │ │ ├── splash.js │ │ └── text.js │ ├── saved-stories.js │ ├── status-bar.js │ ├── stories.js │ ├── stories │ │ ├── no-saved-stories.js │ │ ├── story.js │ │ └── topic-filter.js │ ├── tab-layout.js │ ├── thread.js │ └── thread │ │ ├── collapsible-comment.js │ │ ├── comment.js │ │ ├── comments.js │ │ ├── head.js │ │ ├── load-replies.js │ │ └── no-comments.js ├── connected │ └── thread.connected.js ├── helpers │ ├── comment-helpers.js │ ├── fetch-builder.js │ ├── get-title.js │ └── story-helpers.js ├── index.js ├── layout.js ├── redux │ ├── action-creators │ │ ├── fetch-builder.js │ │ ├── settings.js │ │ ├── stories.js │ │ └── thread.js │ ├── constants.js │ └── reducers │ │ ├── index.js │ │ ├── saved-stories.js │ │ ├── settings.js │ │ ├── stories.js │ │ └── thread.js └── store.js ├── package.json ├── pre-.commit.sh └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 7, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | "jsx": true, 7 | } 8 | }, 9 | "plugins": [ 10 | "react", 11 | "react-native" 12 | ], 13 | "extends": ["eslint:recommended", "plugin:react/recommended"], 14 | "rules": { 15 | "react-native/no-unused-styles": 2 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.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 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | emoji=true 26 | 27 | module.system=haste 28 | 29 | experimental.strict_type_args=true 30 | 31 | munge_underscores=true 32 | 33 | 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' 34 | 35 | suppress_type=$FlowIssue 36 | suppress_type=$FlowFixMe 37 | suppress_type=$FixMe 38 | 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 41 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 42 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 43 | 44 | unsafe.enable_getters_and_setters=true 45 | 46 | [version] 47 | ^0.40.0 48 | -------------------------------------------------------------------------------- /.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://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### hNews 2 | 3 | A Hacker News reader built with React Native 4 | 5 | Checkout the Node.js server [here](https://github.com/seanyesmunt/hackernews-server) 6 | 7 | ~~Download the iOS app here~~ (Currently trying to get v2 published.) 8 | 9 | 10 | ### Installation 11 | 12 | 1. Clone this repo 13 | 14 | 2. `npm i` 15 | 16 | 3. `react-native link` 17 | 18 | 4. `react-native run-ios` 19 | 20 | 21 | ### Contributing 22 | 23 | If you find an issue, feel free to let me know by opening an issue or making a pull request. 24 | 25 | You can also talk to me on Twitter [@seanyesmunt](https://twitter.com/seanyesmunt) 26 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hnews", 3 | "displayName": "hnews" 4 | } -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native' 2 | import App from './js' 3 | 4 | AppRegistry.registerComponent('hnews', () => App) 5 | -------------------------------------------------------------------------------- /ios/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ios/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ios/hnews.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 | 04E204B946984AD69EDAAD46 /* libSafariViewManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C2913F7B76847FE9FD284A5 /* libSafariViewManager.a */; }; 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 | 13B07FBF1A68108700A75B9A /* LaunchImage.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* LaunchImage.xcassets */; }; 21 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 22 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 23 | 1ABDD5212F4E49D1990224A5 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CEBA0AE944364CD4A696AEB4 /* FontAwesome.ttf */; }; 24 | 2861A8759BBB4ED2879B1FF0 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4C97DBDFD1A64B50A580623C /* MaterialCommunityIcons.ttf */; }; 25 | 2958AA422E2B479E9C18657B /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 728F18C99AEB481DB6ED7939 /* SimpleLineIcons.ttf */; }; 26 | 2EC329B4155B4821BB20F8E8 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C35103E179E04102B210A345 /* MaterialIcons.ttf */; }; 27 | 77ACA9CA169844B1802773ED /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 56B5660782AF4158856DC69B /* Foundation.ttf */; }; 28 | 7DA1FC1ABB0943D0B60DCE7A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A7407BF86C6F417A88BFC411 /* libRNVectorIcons.a */; }; 29 | 804C8D731F777B6B00AEDE04 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 30 | 804C8DD31F77875500AEDE04 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 804C8DD21F77875500AEDE04 /* LaunchScreen.xib */; }; 31 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 32 | ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; }; 33 | BA4B7F1A8BFE4B8784C92365 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5CD9E153B22741959470A798 /* Entypo.ttf */; }; 34 | C81E7DAA4C424D14B300ECCB /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F26A2AB4722E4348A2CBDE52 /* Feather.ttf */; }; 35 | CBEF897CA02D4C888FD8B41F /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9362CA914B9F436299812AA0 /* EvilIcons.ttf */; }; 36 | DF1084446B9942C7973E2D11 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E8CCE983828B479689319781 /* Ionicons.ttf */; }; 37 | E318B91F7F064A74AFE19902 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1643C8BF65724071A2E88BAD /* Zocial.ttf */; }; 38 | FC598745CF9E45EBBDF4AA1B /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8074F37DC0E0408F8318CCE5 /* Octicons.ttf */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 45 | proxyType = 2; 46 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 47 | remoteInfo = RCTActionSheet; 48 | }; 49 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 52 | proxyType = 2; 53 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 54 | remoteInfo = RCTGeolocation; 55 | }; 56 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 59 | proxyType = 2; 60 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 61 | remoteInfo = RCTImage; 62 | }; 63 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { 64 | isa = PBXContainerItemProxy; 65 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 66 | proxyType = 2; 67 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 68 | remoteInfo = RCTNetwork; 69 | }; 70 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 71 | isa = PBXContainerItemProxy; 72 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 73 | proxyType = 2; 74 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 75 | remoteInfo = RCTVibration; 76 | }; 77 | 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { 78 | isa = PBXContainerItemProxy; 79 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 80 | proxyType = 2; 81 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 82 | remoteInfo = RCTSettings; 83 | }; 84 | 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { 85 | isa = PBXContainerItemProxy; 86 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 87 | proxyType = 2; 88 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A; 89 | remoteInfo = RCTWebSocket; 90 | }; 91 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 92 | isa = PBXContainerItemProxy; 93 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 94 | proxyType = 2; 95 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 96 | remoteInfo = React; 97 | }; 98 | 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { 99 | isa = PBXContainerItemProxy; 100 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 101 | proxyType = 2; 102 | remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; 103 | remoteInfo = "RCTImage-tvOS"; 104 | }; 105 | 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { 106 | isa = PBXContainerItemProxy; 107 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 108 | proxyType = 2; 109 | remoteGlobalIDString = 2D2A28471D9B043800D4039D; 110 | remoteInfo = "RCTLinking-tvOS"; 111 | }; 112 | 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 113 | isa = PBXContainerItemProxy; 114 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 115 | proxyType = 2; 116 | remoteGlobalIDString = 2D2A28541D9B044C00D4039D; 117 | remoteInfo = "RCTNetwork-tvOS"; 118 | }; 119 | 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 120 | isa = PBXContainerItemProxy; 121 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 122 | proxyType = 2; 123 | remoteGlobalIDString = 2D2A28611D9B046600D4039D; 124 | remoteInfo = "RCTSettings-tvOS"; 125 | }; 126 | 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { 127 | isa = PBXContainerItemProxy; 128 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 129 | proxyType = 2; 130 | remoteGlobalIDString = 2D2A287B1D9B048500D4039D; 131 | remoteInfo = "RCTText-tvOS"; 132 | }; 133 | 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { 134 | isa = PBXContainerItemProxy; 135 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 136 | proxyType = 2; 137 | remoteGlobalIDString = 2D2A28881D9B049200D4039D; 138 | remoteInfo = "RCTWebSocket-tvOS"; 139 | }; 140 | 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { 141 | isa = PBXContainerItemProxy; 142 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 143 | proxyType = 2; 144 | remoteGlobalIDString = 2D2A28131D9B038B00D4039D; 145 | remoteInfo = "React-tvOS"; 146 | }; 147 | 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { 148 | isa = PBXContainerItemProxy; 149 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 150 | proxyType = 2; 151 | remoteGlobalIDString = 3D3C059A1DE3340900C268FA; 152 | remoteInfo = yoga; 153 | }; 154 | 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { 155 | isa = PBXContainerItemProxy; 156 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 157 | proxyType = 2; 158 | remoteGlobalIDString = 3D3C06751DE3340C00C268FA; 159 | remoteInfo = "yoga-tvOS"; 160 | }; 161 | 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { 162 | isa = PBXContainerItemProxy; 163 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 164 | proxyType = 2; 165 | remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; 166 | remoteInfo = cxxreact; 167 | }; 168 | 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 169 | isa = PBXContainerItemProxy; 170 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 171 | proxyType = 2; 172 | remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; 173 | remoteInfo = "cxxreact-tvOS"; 174 | }; 175 | 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 176 | isa = PBXContainerItemProxy; 177 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 178 | proxyType = 2; 179 | remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; 180 | remoteInfo = jschelpers; 181 | }; 182 | 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 183 | isa = PBXContainerItemProxy; 184 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 185 | proxyType = 2; 186 | remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; 187 | remoteInfo = "jschelpers-tvOS"; 188 | }; 189 | 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { 190 | isa = PBXContainerItemProxy; 191 | containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 192 | proxyType = 2; 193 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 194 | remoteInfo = RCTAnimation; 195 | }; 196 | 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { 197 | isa = PBXContainerItemProxy; 198 | containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 199 | proxyType = 2; 200 | remoteGlobalIDString = 2D2A28201D9B03D100D4039D; 201 | remoteInfo = "RCTAnimation-tvOS"; 202 | }; 203 | 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { 204 | isa = PBXContainerItemProxy; 205 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 206 | proxyType = 2; 207 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 208 | remoteInfo = RCTLinking; 209 | }; 210 | 804C8D931F777B6B00AEDE04 /* PBXContainerItemProxy */ = { 211 | isa = PBXContainerItemProxy; 212 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 213 | proxyType = 2; 214 | remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; 215 | remoteInfo = "third-party"; 216 | }; 217 | 804C8D951F777B6B00AEDE04 /* PBXContainerItemProxy */ = { 218 | isa = PBXContainerItemProxy; 219 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 220 | proxyType = 2; 221 | remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; 222 | remoteInfo = "third-party-tvOS"; 223 | }; 224 | 804C8D971F777B6B00AEDE04 /* PBXContainerItemProxy */ = { 225 | isa = PBXContainerItemProxy; 226 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 227 | proxyType = 2; 228 | remoteGlobalIDString = 139D7E881E25C6D100323FB7; 229 | remoteInfo = "double-conversion"; 230 | }; 231 | 804C8D991F777B6B00AEDE04 /* PBXContainerItemProxy */ = { 232 | isa = PBXContainerItemProxy; 233 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 234 | proxyType = 2; 235 | remoteGlobalIDString = 3D383D621EBD27B9005632C8; 236 | remoteInfo = "double-conversion-tvOS"; 237 | }; 238 | 80B945101F77799500DD3A8B /* PBXContainerItemProxy */ = { 239 | isa = PBXContainerItemProxy; 240 | containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; 241 | proxyType = 2; 242 | remoteGlobalIDString = ADD01A681E09402E00F6D226; 243 | remoteInfo = "RCTBlob-tvOS"; 244 | }; 245 | 80B945211F77799500DD3A8B /* PBXContainerItemProxy */ = { 246 | isa = PBXContainerItemProxy; 247 | containerPortal = 38946690799D41649226945C /* RNVectorIcons.xcodeproj */; 248 | proxyType = 2; 249 | remoteGlobalIDString = 5DBEB1501B18CEA900B34395; 250 | remoteInfo = RNVectorIcons; 251 | }; 252 | 80B945241F77799500DD3A8B /* PBXContainerItemProxy */ = { 253 | isa = PBXContainerItemProxy; 254 | containerPortal = 36D428198DDE45749C94B68F /* SafariViewManager.xcodeproj */; 255 | proxyType = 2; 256 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 257 | remoteInfo = SafariViewManager; 258 | }; 259 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 260 | isa = PBXContainerItemProxy; 261 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 262 | proxyType = 2; 263 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 264 | remoteInfo = RCTText; 265 | }; 266 | ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = { 267 | isa = PBXContainerItemProxy; 268 | containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; 269 | proxyType = 2; 270 | remoteGlobalIDString = 358F4ED71D1E81A9004DF814; 271 | remoteInfo = RCTBlob; 272 | }; 273 | /* End PBXContainerItemProxy section */ 274 | 275 | /* Begin PBXFileReference section */ 276 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; 277 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; 278 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; 279 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 280 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 281 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 282 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 283 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 284 | 13B07F961A680F5B00A75B9A /* hnews.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = hnews.app; sourceTree = BUILT_PRODUCTS_DIR; }; 285 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = hnews/AppDelegate.h; sourceTree = ""; }; 286 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = hnews/AppDelegate.m; sourceTree = ""; }; 287 | 13B07FB51A68108700A75B9A /* LaunchImage.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = LaunchImage.xcassets; path = hnews/LaunchImage.xcassets; sourceTree = ""; }; 288 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = hnews/Info.plist; sourceTree = ""; }; 289 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = hnews/main.m; sourceTree = ""; }; 290 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 291 | 1643C8BF65724071A2E88BAD /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; }; 292 | 36D428198DDE45749C94B68F /* SafariViewManager.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = SafariViewManager.xcodeproj; path = "../node_modules/react-native-safari-view/SafariViewManager.xcodeproj"; sourceTree = ""; }; 293 | 38946690799D41649226945C /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = ""; }; 294 | 4C97DBDFD1A64B50A580623C /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = ""; }; 295 | 56B5660782AF4158856DC69B /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; 296 | 5CD9E153B22741959470A798 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; }; 297 | 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 298 | 728F18C99AEB481DB6ED7939 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; 299 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 300 | 7C2913F7B76847FE9FD284A5 /* libSafariViewManager.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libSafariViewManager.a; sourceTree = ""; }; 301 | 804C8DD21F77875500AEDE04 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 302 | 8074F37DC0E0408F8318CCE5 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; }; 303 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 304 | 9362CA914B9F436299812AA0 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; 305 | A7407BF86C6F417A88BFC411 /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = ""; }; 306 | ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = ""; }; 307 | C35103E179E04102B210A345 /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = ""; }; 308 | CEBA0AE944364CD4A696AEB4 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; 309 | E8CCE983828B479689319781 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = ""; }; 310 | F26A2AB4722E4348A2CBDE52 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = ""; }; 311 | /* End PBXFileReference section */ 312 | 313 | /* Begin PBXFrameworksBuildPhase section */ 314 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 315 | isa = PBXFrameworksBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */, 319 | 804C8D731F777B6B00AEDE04 /* libRCTAnimation.a in Frameworks */, 320 | 146834051AC3E58100842450 /* libReact.a in Frameworks */, 321 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 322 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 323 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 324 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, 325 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 326 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 327 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 328 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 329 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 330 | 04E204B946984AD69EDAAD46 /* libSafariViewManager.a in Frameworks */, 331 | 7DA1FC1ABB0943D0B60DCE7A /* libRNVectorIcons.a in Frameworks */, 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | /* End PBXFrameworksBuildPhase section */ 336 | 337 | /* Begin PBXGroup section */ 338 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = { 339 | isa = PBXGroup; 340 | children = ( 341 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 342 | ); 343 | name = Products; 344 | sourceTree = ""; 345 | }; 346 | 00C302B61ABCB90400DB3ED1 /* Products */ = { 347 | isa = PBXGroup; 348 | children = ( 349 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 350 | ); 351 | name = Products; 352 | sourceTree = ""; 353 | }; 354 | 00C302BC1ABCB91800DB3ED1 /* Products */ = { 355 | isa = PBXGroup; 356 | children = ( 357 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 358 | 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, 359 | ); 360 | name = Products; 361 | sourceTree = ""; 362 | }; 363 | 00C302D41ABCB9D200DB3ED1 /* Products */ = { 364 | isa = PBXGroup; 365 | children = ( 366 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 367 | 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, 368 | ); 369 | name = Products; 370 | sourceTree = ""; 371 | }; 372 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = { 373 | isa = PBXGroup; 374 | children = ( 375 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 376 | ); 377 | name = Products; 378 | sourceTree = ""; 379 | }; 380 | 139105B71AF99BAD00B5F7CC /* Products */ = { 381 | isa = PBXGroup; 382 | children = ( 383 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 384 | 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, 385 | ); 386 | name = Products; 387 | sourceTree = ""; 388 | }; 389 | 139FDEE71B06529A00C62182 /* Products */ = { 390 | isa = PBXGroup; 391 | children = ( 392 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 393 | 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, 394 | ); 395 | name = Products; 396 | sourceTree = ""; 397 | }; 398 | 13B07FAE1A68108700A75B9A /* hnews */ = { 399 | isa = PBXGroup; 400 | children = ( 401 | 804C8DD21F77875500AEDE04 /* LaunchScreen.xib */, 402 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 403 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 404 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 405 | 13B07FB51A68108700A75B9A /* LaunchImage.xcassets */, 406 | 13B07FB61A68108700A75B9A /* Info.plist */, 407 | 13B07FB71A68108700A75B9A /* main.m */, 408 | ); 409 | name = hnews; 410 | sourceTree = ""; 411 | }; 412 | 146834001AC3E56700842450 /* Products */ = { 413 | isa = PBXGroup; 414 | children = ( 415 | 146834041AC3E56700842450 /* libReact.a */, 416 | 3DAD3EA31DF850E9000B6D8A /* libReact.a */, 417 | 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, 418 | 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, 419 | 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, 420 | 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 421 | 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, 422 | 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, 423 | 804C8D941F777B6B00AEDE04 /* libthird-party.a */, 424 | 804C8D961F777B6B00AEDE04 /* libthird-party.a */, 425 | 804C8D981F777B6B00AEDE04 /* libdouble-conversion.a */, 426 | 804C8D9A1F777B6B00AEDE04 /* libdouble-conversion.a */, 427 | ); 428 | name = Products; 429 | sourceTree = ""; 430 | }; 431 | 5E91572E1DD0AC6500FF2AA8 /* Products */ = { 432 | isa = PBXGroup; 433 | children = ( 434 | 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, 435 | 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, 436 | ); 437 | name = Products; 438 | sourceTree = ""; 439 | }; 440 | 66051CFE206D4A0E8A134B4B /* Resources */ = { 441 | isa = PBXGroup; 442 | children = ( 443 | 5CD9E153B22741959470A798 /* Entypo.ttf */, 444 | 9362CA914B9F436299812AA0 /* EvilIcons.ttf */, 445 | F26A2AB4722E4348A2CBDE52 /* Feather.ttf */, 446 | CEBA0AE944364CD4A696AEB4 /* FontAwesome.ttf */, 447 | 56B5660782AF4158856DC69B /* Foundation.ttf */, 448 | E8CCE983828B479689319781 /* Ionicons.ttf */, 449 | 4C97DBDFD1A64B50A580623C /* MaterialCommunityIcons.ttf */, 450 | C35103E179E04102B210A345 /* MaterialIcons.ttf */, 451 | 8074F37DC0E0408F8318CCE5 /* Octicons.ttf */, 452 | 728F18C99AEB481DB6ED7939 /* SimpleLineIcons.ttf */, 453 | 1643C8BF65724071A2E88BAD /* Zocial.ttf */, 454 | ); 455 | name = Resources; 456 | sourceTree = ""; 457 | }; 458 | 78C398B11ACF4ADC00677621 /* Products */ = { 459 | isa = PBXGroup; 460 | children = ( 461 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 462 | 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, 463 | ); 464 | name = Products; 465 | sourceTree = ""; 466 | }; 467 | 80B945071F77799400DD3A8B /* Products */ = { 468 | isa = PBXGroup; 469 | children = ( 470 | 80B945251F77799500DD3A8B /* libSafariViewManager.a */, 471 | ); 472 | name = Products; 473 | sourceTree = ""; 474 | }; 475 | 80B945091F77799400DD3A8B /* Products */ = { 476 | isa = PBXGroup; 477 | children = ( 478 | 80B945221F77799500DD3A8B /* libRNVectorIcons.a */, 479 | ); 480 | name = Products; 481 | sourceTree = ""; 482 | }; 483 | 80FD076A1F78662500D8507E /* Recovered References */ = { 484 | isa = PBXGroup; 485 | children = ( 486 | 7C2913F7B76847FE9FD284A5 /* libSafariViewManager.a */, 487 | A7407BF86C6F417A88BFC411 /* libRNVectorIcons.a */, 488 | ); 489 | name = "Recovered References"; 490 | sourceTree = ""; 491 | }; 492 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 493 | isa = PBXGroup; 494 | children = ( 495 | 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 496 | 146833FF1AC3E56700842450 /* React.xcodeproj */, 497 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 498 | ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */, 499 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 500 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 501 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 502 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 503 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 504 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 505 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 506 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 507 | 36D428198DDE45749C94B68F /* SafariViewManager.xcodeproj */, 508 | 38946690799D41649226945C /* RNVectorIcons.xcodeproj */, 509 | ); 510 | name = Libraries; 511 | sourceTree = ""; 512 | }; 513 | 832341B11AAA6A8300B99B32 /* Products */ = { 514 | isa = PBXGroup; 515 | children = ( 516 | 832341B51AAA6A8300B99B32 /* libRCTText.a */, 517 | 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, 518 | ); 519 | name = Products; 520 | sourceTree = ""; 521 | }; 522 | 83CBB9F61A601CBA00E9B192 = { 523 | isa = PBXGroup; 524 | children = ( 525 | 13B07FAE1A68108700A75B9A /* hnews */, 526 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 527 | 83CBBA001A601CBA00E9B192 /* Products */, 528 | 66051CFE206D4A0E8A134B4B /* Resources */, 529 | 80FD076A1F78662500D8507E /* Recovered References */, 530 | ); 531 | indentWidth = 2; 532 | sourceTree = ""; 533 | tabWidth = 2; 534 | usesTabs = 0; 535 | }; 536 | 83CBBA001A601CBA00E9B192 /* Products */ = { 537 | isa = PBXGroup; 538 | children = ( 539 | 13B07F961A680F5B00A75B9A /* hnews.app */, 540 | ); 541 | name = Products; 542 | sourceTree = ""; 543 | }; 544 | ADBDB9201DFEBF0600ED6528 /* Products */ = { 545 | isa = PBXGroup; 546 | children = ( 547 | ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */, 548 | 80B945111F77799500DD3A8B /* libRCTBlob-tvOS.a */, 549 | ); 550 | name = Products; 551 | sourceTree = ""; 552 | }; 553 | /* End PBXGroup section */ 554 | 555 | /* Begin PBXNativeTarget section */ 556 | 13B07F861A680F5B00A75B9A /* hnews */ = { 557 | isa = PBXNativeTarget; 558 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "hnews" */; 559 | buildPhases = ( 560 | 13B07F871A680F5B00A75B9A /* Sources */, 561 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 562 | 13B07F8E1A680F5B00A75B9A /* Resources */, 563 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 564 | ); 565 | buildRules = ( 566 | ); 567 | dependencies = ( 568 | ); 569 | name = hnews; 570 | productName = "Hello World"; 571 | productReference = 13B07F961A680F5B00A75B9A /* hnews.app */; 572 | productType = "com.apple.product-type.application"; 573 | }; 574 | /* End PBXNativeTarget section */ 575 | 576 | /* Begin PBXProject section */ 577 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 578 | isa = PBXProject; 579 | attributes = { 580 | LastUpgradeCheck = 0900; 581 | ORGANIZATIONNAME = Facebook; 582 | TargetAttributes = { 583 | 13B07F861A680F5B00A75B9A = { 584 | DevelopmentTeam = 348S7987BH; 585 | }; 586 | }; 587 | }; 588 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "hnews" */; 589 | compatibilityVersion = "Xcode 3.2"; 590 | developmentRegion = English; 591 | hasScannedForEncodings = 0; 592 | knownRegions = ( 593 | en, 594 | Base, 595 | ); 596 | mainGroup = 83CBB9F61A601CBA00E9B192; 597 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 598 | projectDirPath = ""; 599 | projectReferences = ( 600 | { 601 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; 602 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 603 | }, 604 | { 605 | ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; 606 | ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 607 | }, 608 | { 609 | ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */; 610 | ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; 611 | }, 612 | { 613 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; 614 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 615 | }, 616 | { 617 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; 618 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 619 | }, 620 | { 621 | ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; 622 | ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 623 | }, 624 | { 625 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; 626 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 627 | }, 628 | { 629 | ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; 630 | ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 631 | }, 632 | { 633 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; 634 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 635 | }, 636 | { 637 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; 638 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 639 | }, 640 | { 641 | ProductGroup = 139FDEE71B06529A00C62182 /* Products */; 642 | ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 643 | }, 644 | { 645 | ProductGroup = 146834001AC3E56700842450 /* Products */; 646 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; 647 | }, 648 | { 649 | ProductGroup = 80B945091F77799400DD3A8B /* Products */; 650 | ProjectRef = 38946690799D41649226945C /* RNVectorIcons.xcodeproj */; 651 | }, 652 | { 653 | ProductGroup = 80B945071F77799400DD3A8B /* Products */; 654 | ProjectRef = 36D428198DDE45749C94B68F /* SafariViewManager.xcodeproj */; 655 | }, 656 | ); 657 | projectRoot = ""; 658 | targets = ( 659 | 13B07F861A680F5B00A75B9A /* hnews */, 660 | ); 661 | }; 662 | /* End PBXProject section */ 663 | 664 | /* Begin PBXReferenceProxy section */ 665 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 666 | isa = PBXReferenceProxy; 667 | fileType = archive.ar; 668 | path = libRCTActionSheet.a; 669 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; 670 | sourceTree = BUILT_PRODUCTS_DIR; 671 | }; 672 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { 673 | isa = PBXReferenceProxy; 674 | fileType = archive.ar; 675 | path = libRCTGeolocation.a; 676 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; 677 | sourceTree = BUILT_PRODUCTS_DIR; 678 | }; 679 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 680 | isa = PBXReferenceProxy; 681 | fileType = archive.ar; 682 | path = libRCTImage.a; 683 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; 684 | sourceTree = BUILT_PRODUCTS_DIR; 685 | }; 686 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 687 | isa = PBXReferenceProxy; 688 | fileType = archive.ar; 689 | path = libRCTNetwork.a; 690 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; 691 | sourceTree = BUILT_PRODUCTS_DIR; 692 | }; 693 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 694 | isa = PBXReferenceProxy; 695 | fileType = archive.ar; 696 | path = libRCTVibration.a; 697 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; 698 | sourceTree = BUILT_PRODUCTS_DIR; 699 | }; 700 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { 701 | isa = PBXReferenceProxy; 702 | fileType = archive.ar; 703 | path = libRCTSettings.a; 704 | remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; 705 | sourceTree = BUILT_PRODUCTS_DIR; 706 | }; 707 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { 708 | isa = PBXReferenceProxy; 709 | fileType = archive.ar; 710 | path = libRCTWebSocket.a; 711 | remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; 712 | sourceTree = BUILT_PRODUCTS_DIR; 713 | }; 714 | 146834041AC3E56700842450 /* libReact.a */ = { 715 | isa = PBXReferenceProxy; 716 | fileType = archive.ar; 717 | path = libReact.a; 718 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; 719 | sourceTree = BUILT_PRODUCTS_DIR; 720 | }; 721 | 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { 722 | isa = PBXReferenceProxy; 723 | fileType = archive.ar; 724 | path = "libRCTImage-tvOS.a"; 725 | remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; 726 | sourceTree = BUILT_PRODUCTS_DIR; 727 | }; 728 | 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { 729 | isa = PBXReferenceProxy; 730 | fileType = archive.ar; 731 | path = "libRCTLinking-tvOS.a"; 732 | remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; 733 | sourceTree = BUILT_PRODUCTS_DIR; 734 | }; 735 | 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { 736 | isa = PBXReferenceProxy; 737 | fileType = archive.ar; 738 | path = "libRCTNetwork-tvOS.a"; 739 | remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; 740 | sourceTree = BUILT_PRODUCTS_DIR; 741 | }; 742 | 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { 743 | isa = PBXReferenceProxy; 744 | fileType = archive.ar; 745 | path = "libRCTSettings-tvOS.a"; 746 | remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; 747 | sourceTree = BUILT_PRODUCTS_DIR; 748 | }; 749 | 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { 750 | isa = PBXReferenceProxy; 751 | fileType = archive.ar; 752 | path = "libRCTText-tvOS.a"; 753 | remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; 754 | sourceTree = BUILT_PRODUCTS_DIR; 755 | }; 756 | 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { 757 | isa = PBXReferenceProxy; 758 | fileType = archive.ar; 759 | path = "libRCTWebSocket-tvOS.a"; 760 | remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; 761 | sourceTree = BUILT_PRODUCTS_DIR; 762 | }; 763 | 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { 764 | isa = PBXReferenceProxy; 765 | fileType = archive.ar; 766 | path = libReact.a; 767 | remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; 768 | sourceTree = BUILT_PRODUCTS_DIR; 769 | }; 770 | 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { 771 | isa = PBXReferenceProxy; 772 | fileType = archive.ar; 773 | path = libyoga.a; 774 | remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; 775 | sourceTree = BUILT_PRODUCTS_DIR; 776 | }; 777 | 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { 778 | isa = PBXReferenceProxy; 779 | fileType = archive.ar; 780 | path = libyoga.a; 781 | remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; 782 | sourceTree = BUILT_PRODUCTS_DIR; 783 | }; 784 | 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { 785 | isa = PBXReferenceProxy; 786 | fileType = archive.ar; 787 | path = libcxxreact.a; 788 | remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; 789 | sourceTree = BUILT_PRODUCTS_DIR; 790 | }; 791 | 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { 792 | isa = PBXReferenceProxy; 793 | fileType = archive.ar; 794 | path = libcxxreact.a; 795 | remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; 796 | sourceTree = BUILT_PRODUCTS_DIR; 797 | }; 798 | 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { 799 | isa = PBXReferenceProxy; 800 | fileType = archive.ar; 801 | path = libjschelpers.a; 802 | remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; 803 | sourceTree = BUILT_PRODUCTS_DIR; 804 | }; 805 | 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { 806 | isa = PBXReferenceProxy; 807 | fileType = archive.ar; 808 | path = libjschelpers.a; 809 | remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; 810 | sourceTree = BUILT_PRODUCTS_DIR; 811 | }; 812 | 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { 813 | isa = PBXReferenceProxy; 814 | fileType = archive.ar; 815 | path = libRCTAnimation.a; 816 | remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; 817 | sourceTree = BUILT_PRODUCTS_DIR; 818 | }; 819 | 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { 820 | isa = PBXReferenceProxy; 821 | fileType = archive.ar; 822 | path = libRCTAnimation.a; 823 | remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; 824 | sourceTree = BUILT_PRODUCTS_DIR; 825 | }; 826 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { 827 | isa = PBXReferenceProxy; 828 | fileType = archive.ar; 829 | path = libRCTLinking.a; 830 | remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; 831 | sourceTree = BUILT_PRODUCTS_DIR; 832 | }; 833 | 804C8D941F777B6B00AEDE04 /* libthird-party.a */ = { 834 | isa = PBXReferenceProxy; 835 | fileType = archive.ar; 836 | path = "libthird-party.a"; 837 | remoteRef = 804C8D931F777B6B00AEDE04 /* PBXContainerItemProxy */; 838 | sourceTree = BUILT_PRODUCTS_DIR; 839 | }; 840 | 804C8D961F777B6B00AEDE04 /* libthird-party.a */ = { 841 | isa = PBXReferenceProxy; 842 | fileType = archive.ar; 843 | path = "libthird-party.a"; 844 | remoteRef = 804C8D951F777B6B00AEDE04 /* PBXContainerItemProxy */; 845 | sourceTree = BUILT_PRODUCTS_DIR; 846 | }; 847 | 804C8D981F777B6B00AEDE04 /* libdouble-conversion.a */ = { 848 | isa = PBXReferenceProxy; 849 | fileType = archive.ar; 850 | path = "libdouble-conversion.a"; 851 | remoteRef = 804C8D971F777B6B00AEDE04 /* PBXContainerItemProxy */; 852 | sourceTree = BUILT_PRODUCTS_DIR; 853 | }; 854 | 804C8D9A1F777B6B00AEDE04 /* libdouble-conversion.a */ = { 855 | isa = PBXReferenceProxy; 856 | fileType = archive.ar; 857 | path = "libdouble-conversion.a"; 858 | remoteRef = 804C8D991F777B6B00AEDE04 /* PBXContainerItemProxy */; 859 | sourceTree = BUILT_PRODUCTS_DIR; 860 | }; 861 | 80B945111F77799500DD3A8B /* libRCTBlob-tvOS.a */ = { 862 | isa = PBXReferenceProxy; 863 | fileType = archive.ar; 864 | path = "libRCTBlob-tvOS.a"; 865 | remoteRef = 80B945101F77799500DD3A8B /* PBXContainerItemProxy */; 866 | sourceTree = BUILT_PRODUCTS_DIR; 867 | }; 868 | 80B945221F77799500DD3A8B /* libRNVectorIcons.a */ = { 869 | isa = PBXReferenceProxy; 870 | fileType = archive.ar; 871 | path = libRNVectorIcons.a; 872 | remoteRef = 80B945211F77799500DD3A8B /* PBXContainerItemProxy */; 873 | sourceTree = BUILT_PRODUCTS_DIR; 874 | }; 875 | 80B945251F77799500DD3A8B /* libSafariViewManager.a */ = { 876 | isa = PBXReferenceProxy; 877 | fileType = archive.ar; 878 | path = libSafariViewManager.a; 879 | remoteRef = 80B945241F77799500DD3A8B /* PBXContainerItemProxy */; 880 | sourceTree = BUILT_PRODUCTS_DIR; 881 | }; 882 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 883 | isa = PBXReferenceProxy; 884 | fileType = archive.ar; 885 | path = libRCTText.a; 886 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; 887 | sourceTree = BUILT_PRODUCTS_DIR; 888 | }; 889 | ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = { 890 | isa = PBXReferenceProxy; 891 | fileType = archive.ar; 892 | path = libRCTBlob.a; 893 | remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */; 894 | sourceTree = BUILT_PRODUCTS_DIR; 895 | }; 896 | /* End PBXReferenceProxy section */ 897 | 898 | /* Begin PBXResourcesBuildPhase section */ 899 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 900 | isa = PBXResourcesBuildPhase; 901 | buildActionMask = 2147483647; 902 | files = ( 903 | 13B07FBF1A68108700A75B9A /* LaunchImage.xcassets in Resources */, 904 | BA4B7F1A8BFE4B8784C92365 /* Entypo.ttf in Resources */, 905 | CBEF897CA02D4C888FD8B41F /* EvilIcons.ttf in Resources */, 906 | C81E7DAA4C424D14B300ECCB /* Feather.ttf in Resources */, 907 | 1ABDD5212F4E49D1990224A5 /* FontAwesome.ttf in Resources */, 908 | 77ACA9CA169844B1802773ED /* Foundation.ttf in Resources */, 909 | DF1084446B9942C7973E2D11 /* Ionicons.ttf in Resources */, 910 | 2861A8759BBB4ED2879B1FF0 /* MaterialCommunityIcons.ttf in Resources */, 911 | 2EC329B4155B4821BB20F8E8 /* MaterialIcons.ttf in Resources */, 912 | 804C8DD31F77875500AEDE04 /* LaunchScreen.xib in Resources */, 913 | FC598745CF9E45EBBDF4AA1B /* Octicons.ttf in Resources */, 914 | 2958AA422E2B479E9C18657B /* SimpleLineIcons.ttf in Resources */, 915 | E318B91F7F064A74AFE19902 /* Zocial.ttf in Resources */, 916 | ); 917 | runOnlyForDeploymentPostprocessing = 0; 918 | }; 919 | /* End PBXResourcesBuildPhase section */ 920 | 921 | /* Begin PBXShellScriptBuildPhase section */ 922 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 923 | isa = PBXShellScriptBuildPhase; 924 | buildActionMask = 2147483647; 925 | files = ( 926 | ); 927 | inputPaths = ( 928 | ); 929 | name = "Bundle React Native code and images"; 930 | outputPaths = ( 931 | ); 932 | runOnlyForDeploymentPostprocessing = 0; 933 | shellPath = /bin/sh; 934 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; 935 | }; 936 | /* End PBXShellScriptBuildPhase section */ 937 | 938 | /* Begin PBXSourcesBuildPhase section */ 939 | 13B07F871A680F5B00A75B9A /* Sources */ = { 940 | isa = PBXSourcesBuildPhase; 941 | buildActionMask = 2147483647; 942 | files = ( 943 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 944 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 945 | ); 946 | runOnlyForDeploymentPostprocessing = 0; 947 | }; 948 | /* End PBXSourcesBuildPhase section */ 949 | 950 | /* Begin XCBuildConfiguration section */ 951 | 13B07F941A680F5B00A75B9A /* Debug */ = { 952 | isa = XCBuildConfiguration; 953 | buildSettings = { 954 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 955 | CURRENT_PROJECT_VERSION = 1; 956 | DEAD_CODE_STRIPPING = NO; 957 | DEVELOPMENT_TEAM = 348S7987BH; 958 | HEADER_SEARCH_PATHS = ( 959 | "$(inherited)", 960 | "$(SRCROOT)/../node_modules/react-native-safari-view", 961 | "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", 962 | ); 963 | INFOPLIST_FILE = hnews/Info.plist; 964 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 965 | OTHER_LDFLAGS = ( 966 | "$(inherited)", 967 | "-ObjC", 968 | "-lc++", 969 | ); 970 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; 971 | PRODUCT_NAME = hnews; 972 | VERSIONING_SYSTEM = "apple-generic"; 973 | }; 974 | name = Debug; 975 | }; 976 | 13B07F951A680F5B00A75B9A /* Release */ = { 977 | isa = XCBuildConfiguration; 978 | buildSettings = { 979 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 980 | CURRENT_PROJECT_VERSION = 1; 981 | DEVELOPMENT_TEAM = 348S7987BH; 982 | HEADER_SEARCH_PATHS = ( 983 | "$(inherited)", 984 | "$(SRCROOT)/../node_modules/react-native-safari-view", 985 | "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", 986 | ); 987 | INFOPLIST_FILE = hnews/Info.plist; 988 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 989 | OTHER_LDFLAGS = ( 990 | "$(inherited)", 991 | "-ObjC", 992 | "-lc++", 993 | ); 994 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; 995 | PRODUCT_NAME = hnews; 996 | VERSIONING_SYSTEM = "apple-generic"; 997 | }; 998 | name = Release; 999 | }; 1000 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 1001 | isa = XCBuildConfiguration; 1002 | buildSettings = { 1003 | ALWAYS_SEARCH_USER_PATHS = NO; 1004 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1005 | CLANG_CXX_LIBRARY = "libc++"; 1006 | CLANG_ENABLE_MODULES = YES; 1007 | CLANG_ENABLE_OBJC_ARC = YES; 1008 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1009 | CLANG_WARN_BOOL_CONVERSION = YES; 1010 | CLANG_WARN_COMMA = YES; 1011 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1012 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1013 | CLANG_WARN_EMPTY_BODY = YES; 1014 | CLANG_WARN_ENUM_CONVERSION = YES; 1015 | CLANG_WARN_INFINITE_RECURSION = YES; 1016 | CLANG_WARN_INT_CONVERSION = YES; 1017 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1018 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1019 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1020 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1021 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1022 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1023 | CLANG_WARN_UNREACHABLE_CODE = YES; 1024 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1025 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1026 | COPY_PHASE_STRIP = NO; 1027 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1028 | ENABLE_TESTABILITY = YES; 1029 | GCC_C_LANGUAGE_STANDARD = gnu99; 1030 | GCC_DYNAMIC_NO_PIC = NO; 1031 | GCC_NO_COMMON_BLOCKS = YES; 1032 | GCC_OPTIMIZATION_LEVEL = 0; 1033 | GCC_PREPROCESSOR_DEFINITIONS = ( 1034 | "DEBUG=1", 1035 | "$(inherited)", 1036 | ); 1037 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 1038 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1039 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1040 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1041 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1042 | GCC_WARN_UNUSED_FUNCTION = YES; 1043 | GCC_WARN_UNUSED_VARIABLE = YES; 1044 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 1045 | MTL_ENABLE_DEBUG_INFO = YES; 1046 | ONLY_ACTIVE_ARCH = YES; 1047 | SDKROOT = iphoneos; 1048 | }; 1049 | name = Debug; 1050 | }; 1051 | 83CBBA211A601CBA00E9B192 /* Release */ = { 1052 | isa = XCBuildConfiguration; 1053 | buildSettings = { 1054 | ALWAYS_SEARCH_USER_PATHS = NO; 1055 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1056 | CLANG_CXX_LIBRARY = "libc++"; 1057 | CLANG_ENABLE_MODULES = YES; 1058 | CLANG_ENABLE_OBJC_ARC = YES; 1059 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1060 | CLANG_WARN_BOOL_CONVERSION = YES; 1061 | CLANG_WARN_COMMA = YES; 1062 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1063 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1064 | CLANG_WARN_EMPTY_BODY = YES; 1065 | CLANG_WARN_ENUM_CONVERSION = YES; 1066 | CLANG_WARN_INFINITE_RECURSION = YES; 1067 | CLANG_WARN_INT_CONVERSION = YES; 1068 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1069 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1070 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1071 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1072 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1073 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1074 | CLANG_WARN_UNREACHABLE_CODE = YES; 1075 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1076 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1077 | COPY_PHASE_STRIP = YES; 1078 | ENABLE_NS_ASSERTIONS = NO; 1079 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1080 | GCC_C_LANGUAGE_STANDARD = gnu99; 1081 | GCC_NO_COMMON_BLOCKS = YES; 1082 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1083 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1084 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1085 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1086 | GCC_WARN_UNUSED_FUNCTION = YES; 1087 | GCC_WARN_UNUSED_VARIABLE = YES; 1088 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 1089 | MTL_ENABLE_DEBUG_INFO = NO; 1090 | SDKROOT = iphoneos; 1091 | VALIDATE_PRODUCT = YES; 1092 | }; 1093 | name = Release; 1094 | }; 1095 | /* End XCBuildConfiguration section */ 1096 | 1097 | /* Begin XCConfigurationList section */ 1098 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "hnews" */ = { 1099 | isa = XCConfigurationList; 1100 | buildConfigurations = ( 1101 | 13B07F941A680F5B00A75B9A /* Debug */, 1102 | 13B07F951A680F5B00A75B9A /* Release */, 1103 | ); 1104 | defaultConfigurationIsVisible = 0; 1105 | defaultConfigurationName = Release; 1106 | }; 1107 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "hnews" */ = { 1108 | isa = XCConfigurationList; 1109 | buildConfigurations = ( 1110 | 83CBBA201A601CBA00E9B192 /* Debug */, 1111 | 83CBBA211A601CBA00E9B192 /* Release */, 1112 | ); 1113 | defaultConfigurationIsVisible = 0; 1114 | defaultConfigurationName = Release; 1115 | }; 1116 | /* End XCConfigurationList section */ 1117 | }; 1118 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 1119 | } 1120 | -------------------------------------------------------------------------------- /ios/hnews.xcodeproj/xcshareddata/xcschemes/hnews-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 94 | 96 | 102 | 103 | 104 | 105 | 106 | 107 | 113 | 115 | 121 | 122 | 123 | 124 | 126 | 127 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /ios/hnews.xcodeproj/xcshareddata/xcschemes/hnews.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 94 | 96 | 102 | 103 | 104 | 105 | 106 | 107 | 113 | 115 | 121 | 122 | 123 | 124 | 126 | 127 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /ios/hnews/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/hnews/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | @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:@"hnews" 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/hnews/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | hnews 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 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UIAppFonts 41 | 42 | Entypo.ttf 43 | EvilIcons.ttf 44 | Feather.ttf 45 | FontAwesome.ttf 46 | Foundation.ttf 47 | Ionicons.ttf 48 | MaterialCommunityIcons.ttf 49 | MaterialIcons.ttf 50 | Octicons.ttf 51 | SimpleLineIcons.ttf 52 | Zocial.ttf 53 | 54 | UILaunchStoryboardName 55 | LaunchScreen 56 | UIRequiredDeviceCapabilities 57 | 58 | armv7 59 | 60 | UIStatusBarStyle 61 | UIStatusBarStyleLightContent 62 | UISupportedInterfaceOrientations 63 | 64 | UIInterfaceOrientationPortrait 65 | UIInterfaceOrientationPortraitUpsideDown 66 | 67 | UIViewControllerBasedStatusBarAppearance 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-57x57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-57x57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-App-60x60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-App-60x60@3x.png", 67 | "scale" : "3x" 68 | } 69 | ], 70 | "info" : { 71 | "version" : 1, 72 | "author" : "xcode" 73 | } 74 | } -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neb-b/hackernews/fd3ca655f70a39aa487c2179c1865f8035f25121/ios/hnews/LaunchImage.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/hnews/LaunchImage.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/hnews/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 | -------------------------------------------------------------------------------- /js/app.container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View } from "react-native"; 3 | import { Navigator } from "react-native-deprecated-custom-components"; 4 | import { connect, Provider } from "react-redux"; 5 | import { 6 | fetchInitialData, 7 | changeView, 8 | changeTopic 9 | } from "./redux/action-creators/settings"; 10 | import { 11 | refreshStories, 12 | refreshSavedStories, 13 | saveStory, 14 | unSaveStory 15 | } from "./redux/action-creators/stories"; 16 | import SplashScreen from "./components/generic/splash"; 17 | import Error from "./components/generic/error"; 18 | import Layout from "./layout"; 19 | import SafariView from "react-native-safari-view"; 20 | 21 | class App extends Component { 22 | componentDidMount() { 23 | const { fetchInitialData, topics: { currentlySelected } } = this.props; 24 | fetchInitialData(currentlySelected); 25 | } 26 | 27 | openSafari(url) { 28 | SafariView.show({ 29 | url 30 | }); 31 | } 32 | 33 | render() { 34 | const { 35 | loading, 36 | title, 37 | error, 38 | fetchInitialData, 39 | topics: { currentlySelected } 40 | } = this.props; 41 | 42 | return ( 43 | 44 | {error && fetchInitialData(currentlySelected)} />} 45 | {loading && } 46 | {!loading && 47 | { 50 | const { component, title: routeTitle, index, linkProps } = route; 51 | return ( 52 | SafariView.show({ url })} 61 | /> 62 | ); 63 | }} 64 | />} 65 | 66 | ); 67 | } 68 | } 69 | 70 | const mapStateToProps = ({ settings, stories, savedStories }) => ({ 71 | stories, 72 | savedStories, 73 | ...settings 74 | }); 75 | 76 | export default connect(mapStateToProps, { 77 | fetchInitialData, 78 | changeView, 79 | changeTopic, 80 | refreshStories, 81 | saveStory, 82 | unSaveStory, 83 | refreshStories, 84 | refreshSavedStories 85 | })(App); 86 | -------------------------------------------------------------------------------- /js/components/generic/button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { TouchableHighlight, View, StyleSheet } from "react-native"; 4 | 5 | const Button = ({ 6 | _style, 7 | height, 8 | children, 9 | onPress, 10 | paddedLeft, 11 | paddedRight, 12 | paddedTop, 13 | paddedBottom, 14 | padded, 15 | flex, 16 | underlayColor 17 | }) => { 18 | return ( 19 | 33 | 34 | {children} 35 | 36 | 37 | ); 38 | }; 39 | 40 | Button.defaultProps = { 41 | row: true 42 | }; 43 | 44 | Button.propTypes = { 45 | row: PropTypes.bool 46 | }; 47 | 48 | const styles = StyleSheet.create({ 49 | button: { 50 | // flexDirection: 'row', 51 | // justifyContent: 'center' 52 | }, 53 | row: { 54 | flexDirection: "row" 55 | }, 56 | column: { 57 | flexDirection: "column" 58 | }, 59 | flex: { 60 | flex: 1 61 | }, 62 | padded: { 63 | padding: 10 64 | }, 65 | paddedLeft: { 66 | paddingLeft: 10 67 | }, 68 | paddedRight: { 69 | paddingRight: 10 70 | }, 71 | paddedTop: { 72 | paddingTop: 10 73 | }, 74 | paddedBottom: { 75 | paddingBottom: 10 76 | } 77 | }); 78 | 79 | export default Button; 80 | -------------------------------------------------------------------------------- /js/components/generic/error.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Text from "./text"; 4 | import Button from "./button"; 5 | 6 | const Error = ({ refresh }) => ( 7 | 14 | ); 15 | 16 | const styles = StyleSheet.create({ 17 | error: { 18 | flexDirection: "row", 19 | justifyContent: "center", 20 | backgroundColor: "#fe462c", 21 | padding: 20 22 | } 23 | }); 24 | 25 | export default Error; 26 | -------------------------------------------------------------------------------- /js/components/generic/link.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, View, TouchableHighlight } from "react-native"; 3 | import Thread from "../../connected/thread.connected"; 4 | 5 | const getScene = (title, linkProps) => { 6 | const components = { 7 | Thread 8 | }; 9 | 10 | return { 11 | title: "Comments", 12 | linkProps, 13 | component: components[title] 14 | }; 15 | }; 16 | 17 | const Link = ({ 18 | navigator, 19 | children, 20 | to: newView, 21 | viewIndex, 22 | linkProps, 23 | _style, 24 | underlayColor 25 | }) => ( 26 | 30 | navigator.push( 31 | Object.assign({}, getScene(newView, linkProps), { 32 | index: viewIndex + 1 33 | }) 34 | )} 35 | > 36 | 37 | {children} 38 | 39 | 40 | ); 41 | 42 | const styles = StyleSheet.create({}); 43 | 44 | export default Link; 45 | -------------------------------------------------------------------------------- /js/components/generic/list.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FlatList, RefreshControl, View, StyleSheet } from "react-native"; 3 | 4 | const List = ({ 5 | items, 6 | renderItem, 7 | refresh, 8 | refreshing, 9 | header: Header, 10 | _style 11 | }) => { 12 | return ( 13 | 14 | item.id} 18 | ListHeaderComponent={Header} 19 | renderItem={renderItem} 20 | data={items} 21 | /> 22 | 23 | ); 24 | }; 25 | 26 | const styles = StyleSheet.create({ 27 | list: { 28 | // flex: 1 29 | } 30 | }); 31 | 32 | export default List; 33 | -------------------------------------------------------------------------------- /js/components/generic/loader.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, ActivityIndicator, StyleSheet } from "react-native"; 3 | 4 | const Loader = () => ( 5 | 6 | 7 | 8 | ); 9 | 10 | const styles = StyleSheet.create({ 11 | loader: { 12 | marginTop: 100 13 | } 14 | }); 15 | 16 | export default Loader; 17 | -------------------------------------------------------------------------------- /js/components/generic/splash.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ActivityIndicator, StyleSheet, View } from "react-native"; 3 | import Text from "./text"; 4 | 5 | const Splash = () => ( 6 | 7 | 8 | 9 | Loading stories... 10 | 11 | 12 | ); 13 | 14 | const styles = StyleSheet.create({ 15 | splash: { 16 | flex: 1, 17 | paddingTop: 250, 18 | alignItems: "center", 19 | backgroundColor: "#009688" 20 | }, 21 | message: { 22 | paddingTop: 30 23 | } 24 | }); 25 | 26 | export default Splash; 27 | -------------------------------------------------------------------------------- /js/components/generic/text.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Text as NativeText, StyleSheet } from "react-native"; 3 | 4 | const Text = ({ 5 | children, 6 | bold, 7 | size, 8 | _style, 9 | alignRight, 10 | alignCenter, 11 | color, 12 | paddedTop 13 | }) => ( 14 | 26 | {children} 27 | 28 | ); 29 | 30 | const styles = StyleSheet.create({ 31 | font: { 32 | fontFamily: "AppleSDGothicNeo-Medium" 33 | }, 34 | bold: { 35 | fontFamily: "AppleSDGothicNeo-Bold" 36 | } 37 | }); 38 | 39 | export default Text; 40 | -------------------------------------------------------------------------------- /js/components/saved-stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View } from "react-native"; 3 | import TopicFilter from "./stories/topic-filter"; 4 | import List from "./generic/list"; 5 | import Story from "./stories/story"; 6 | import NoSavedStories from "./stories/no-saved-stories"; 7 | 8 | const Stories = ({ 9 | savedStories, 10 | unSaveStory, 11 | refreshing, 12 | refreshSavedStories, 13 | navigator, 14 | viewIndex, 15 | openSafari, 16 | height, 17 | error 18 | }) => { 19 | return ( 20 | 21 | {error && 22 | 24 | refreshSavedStories(savedStories.map(story => story.id))} 25 | />} 26 | {!!savedStories.length && 27 | 32 | savedStories.length && 33 | refreshSavedStories(savedStories.map(story => story.id))} 34 | renderItem={({ item: story }) => ( 35 | 44 | )} 45 | />} 46 | {!savedStories.length && } 47 | 48 | ); 49 | }; 50 | 51 | export default Stories; 52 | -------------------------------------------------------------------------------- /js/components/status-bar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Text, View, StyleSheet, StatusBar } from "react-native"; 3 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 4 | import Button from "./generic/button"; 5 | import SafariView from "react-native-safari-view"; 6 | 7 | class StatusBarWrapper extends Component { 8 | componentDidMount() { 9 | SafariView.addEventListener("onShow", () => 10 | StatusBar.setBarStyle("dark-content") 11 | ); 12 | SafariView.addEventListener("onDismiss", () => 13 | StatusBar.setBarStyle("light-content") 14 | ); 15 | } 16 | 17 | render() { 18 | const { title, showBackArrow, navigator, isHome } = this.props; 19 | return ( 20 | 21 | 22 | 23 | 24 | {!isHome && 25 | } 33 | 34 | 35 | {title} 36 | 37 | 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | const styles = StyleSheet.create({ 45 | statusBar: { 46 | height: 64, 47 | paddingTop: 25, 48 | backgroundColor: "#009688" 49 | }, 50 | row: { 51 | flexDirection: "row" 52 | }, 53 | item: { 54 | width: 50 55 | }, 56 | titleContainer: { 57 | flex: 1 58 | }, 59 | title: { 60 | fontSize: 20, 61 | color: "white", 62 | textAlign: "center" 63 | }, 64 | icon: { 65 | paddingTop: 3 66 | } 67 | }); 68 | 69 | export default StatusBarWrapper; 70 | -------------------------------------------------------------------------------- /js/components/stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import TopicFilter from "./stories/topic-filter"; 4 | import List from "./generic/list"; 5 | import Error from "./generic/error"; 6 | import Loader from "./generic/loader"; 7 | import Story from "./stories/story"; 8 | 9 | const Stories = ({ 10 | viewIndex, 11 | error, 12 | loading, 13 | stories, 14 | saveStory, 15 | unSaveStory, 16 | topics, 17 | navigator, 18 | changeTopic, 19 | refreshStories, 20 | refreshing, 21 | openSafari, 22 | height 23 | }) => { 24 | return ( 25 | 26 | {error && 27 | refreshStories(topics.currentlySelected)} />} 28 | {!loading && 29 | ( 32 | 33 | )} 34 | items={stories} 35 | refresh={() => refreshStories(topics.currentlySelected)} 36 | refreshing={refreshing} 37 | renderItem={({ item: story }) => ( 38 | 46 | )} 47 | />} 48 | {loading && } 49 | 50 | ); 51 | }; 52 | 53 | export default Stories; 54 | -------------------------------------------------------------------------------- /js/components/stories/no-saved-stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 4 | import Text from "../generic/text"; 5 | 6 | const NoSavedStories = () => ( 7 | 8 | 9 | No saved stories... 10 | 11 | ); 12 | 13 | const styles = StyleSheet.create({ 14 | container: { 15 | justifyContent: "center", 16 | alignItems: "center", 17 | height: 300 18 | } 19 | }); 20 | 21 | export default NoSavedStories; 22 | -------------------------------------------------------------------------------- /js/components/stories/story.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet, TouchableHighlight } from "react-native"; 3 | import Text from "../generic/text"; 4 | import Link from "../generic/link"; 5 | import Button from "../generic/button"; 6 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 7 | import { formatUrl, fromNow } from "../../helpers/story-helpers"; 8 | 9 | const Story = ({ 10 | story, 11 | isSavedView, 12 | saveAction, 13 | navigator, 14 | viewIndex, 15 | openSafari, 16 | height 17 | }) => { 18 | const { title, time, score, descendants, url, saved } = story; 19 | 20 | const WrapperEl = url ? Button : Link; 21 | 22 | return ( 23 | url && openSafari(url)} 25 | to="Thread" 26 | linkProps={{ story, height }} 27 | navigator={navigator} 28 | viewIndex={viewIndex} 29 | > 30 | 31 | 32 | {title} 33 | 45 | 46 | 47 | 48 | 49 | {formatUrl(url)} 50 | {fromNow(time)} 51 | 52 | {score || 0} points 53 | 54 | 55 | 56 | 63 | 64 | {descendants || 0} comments 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | ); 74 | }; 75 | 76 | const styles = StyleSheet.create({ 77 | story: { 78 | paddingLeft: 10, 79 | paddingTop: 15, 80 | paddingBottom: 15, 81 | minHeight: 150, 82 | borderTopWidth: 1, 83 | borderTopColor: "#ebebeb" 84 | }, 85 | row: { 86 | flexDirection: "row", 87 | justifyContent: "space-between" 88 | }, 89 | title: { 90 | flex: 1, 91 | flexWrap: "wrap" 92 | }, 93 | saveAction: { 94 | paddingLeft: 20, 95 | marginTop: -10 96 | }, 97 | postInfo: { 98 | paddingTop: 30 99 | }, 100 | comments: { 101 | padding: 10, 102 | marginTop: -10, 103 | flexDirection: "row" 104 | }, 105 | chevronRight: { 106 | marginTop: -1 107 | // marginRight: -15, 108 | // marginLeft: -20 109 | } 110 | }); 111 | 112 | export default Story; 113 | -------------------------------------------------------------------------------- /js/components/stories/topic-filter.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 4 | import Button from "../generic/button"; 5 | import Text from "../generic/text"; 6 | import { titles } from "../../helpers/get-title"; 7 | import Collapsible from "react-native-collapsible"; 8 | 9 | class TopicFilter extends Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.state = { isCollapsed: true }; 14 | } 15 | 16 | render() { 17 | const { topics, changeTopic } = this.props; 18 | const { currentlySelected } = topics; 19 | 20 | return ( 21 | 22 | 38 | 39 | 40 | {topics.available.map(topic => { 41 | const isSelected = topic === currentlySelected; 42 | return ( 43 | 47 | 63 | 64 | ); 65 | })} 66 | 67 | 68 | 69 | ); 70 | } 71 | } 72 | 73 | const styles = StyleSheet.create({ 74 | container: { 75 | flexDirection: "row" 76 | }, 77 | buttonText: { 78 | alignSelf: "flex-end", 79 | flexDirection: "row", 80 | paddingTop: 5 81 | }, 82 | dropDownIcon: { 83 | marginTop: -1 84 | }, 85 | topic: { 86 | textAlign: "right" 87 | }, 88 | selectedContainer: { 89 | backgroundColor: "#169f8430" 90 | }, 91 | selected: { 92 | color: "#16a085" 93 | } 94 | }); 95 | 96 | export default TopicFilter; 97 | -------------------------------------------------------------------------------- /js/components/tab-layout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, TabBarIOS } from "react-native"; 3 | import Icon from "react-native-vector-icons/MaterialCommunityIcons"; 4 | import Stories from "../components/stories"; 5 | import SavedStories from "../components/saved-stories"; 6 | 7 | const TabBarLayout = ({ 8 | viewIndex, 9 | changeView, 10 | viewingStories, 11 | navigator, 12 | topics, 13 | stories, 14 | savedStories, 15 | saveStory, 16 | unSaveStory, 17 | changeTopic, 18 | refreshStories, 19 | refreshSavedStories, 20 | openSafari, 21 | height 22 | }) => { 23 | return ( 24 | 30 | viewingStories && changeView()} 35 | > 36 | 46 | 47 | !viewingStories && changeView()} 52 | > 53 | 65 | 66 | 67 | ); 68 | }; 69 | 70 | export default TabBarLayout; 71 | -------------------------------------------------------------------------------- /js/components/thread.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ActivityIndicator, View, StyleSheet } from "react-native"; 3 | import Loader from "./generic/loader"; 4 | import Head from "./thread/head"; 5 | import Comments from "./thread/comments"; 6 | import Error from "./generic/error"; 7 | 8 | const Thread = ({ 9 | loading, 10 | story, 11 | comments, 12 | refreshThread, 13 | refreshing, 14 | loadComments, 15 | fetchingReplies, 16 | fetchingRepliesFor, 17 | loadReplies, 18 | toggleComment, 19 | saveStory, 20 | unSaveStory, 21 | openSafari, 22 | height, 23 | error 24 | }) => { 25 | const { saved, title, score, time, descendants, url } = story; 26 | 27 | const _renderHead = () => ( 28 | 39 | ); 40 | 41 | return ( 42 | 43 | {error && 44 | refreshThread(comments.map(comment => comment.id))} 46 | />} 47 | {loading && _renderHead()} 48 | {loading && } 49 | {!loading && 50 | } 63 | 64 | ); 65 | }; 66 | 67 | const styles = StyleSheet.create({ 68 | thread: { 69 | flex: 1, 70 | backgroundColor: "white" 71 | } 72 | }); 73 | 74 | export default Thread; 75 | -------------------------------------------------------------------------------- /js/components/thread/collapsible-comment.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import HTMLView from "react-native-htmlview"; 4 | import LoadReplies from "./load-replies"; 5 | import { fromNow } from "../../helpers/story-helpers"; 6 | import Collapsible from "react-native-collapsible"; 7 | import Comment from "./comment"; 8 | 9 | const CollapsibleComment = ({ 10 | expanded, 11 | text, 12 | kids, 13 | fetchingRepliesFor, 14 | id, 15 | commentChain, 16 | loadReplies, 17 | openSafari 18 | }) => { 19 | const replies = kids && kids.length; 20 | return ( 21 | 22 | 23 | 24 | ${text}`} 26 | onLinkPress={url => openSafari(url)} 27 | stylesheet={styles} 28 | /> 29 | {replies && 30 | typeof kids[0] === "number" && 31 | } 38 | {replies && 39 | typeof kids[0] === "object" && 40 | 41 | {kids.map(comment => ( 42 | 49 | ))} 50 | } 51 | 52 | 53 | 54 | ); 55 | }; 56 | 57 | const styles = StyleSheet.create({ 58 | container: { 59 | // paddingBottom: 10 60 | }, 61 | repliesContainer: { 62 | marginTop: 10 63 | }, 64 | custom: { 65 | // lineHeight: 1 66 | }, 67 | p: { 68 | lineHeight: 18, 69 | fontSize: 14 70 | } 71 | }); 72 | 73 | export default CollapsibleComment; 74 | -------------------------------------------------------------------------------- /js/components/thread/comment.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Animated, View, StyleSheet } from "react-native"; 3 | import HTMLView from "react-native-htmlview"; 4 | import moment from "moment"; 5 | import Button from "../generic/button"; 6 | import Text from "../generic/text"; 7 | import LoadReplies from "./load-replies"; 8 | import { fromNow } from "../../helpers/story-helpers"; 9 | import CollapsibleComment from "./collapsible-comment"; 10 | import Collapsible from "react-native-collapsible"; 11 | 12 | class Comment extends Component { 13 | constructor(props) { 14 | super(props); 15 | 16 | this.state = { 17 | expanded: true 18 | }; 19 | } 20 | 21 | _toggleComment() { 22 | this.setState({ 23 | expanded: !this.state.expanded 24 | }); 25 | } 26 | 27 | render() { 28 | const { 29 | text, 30 | by, 31 | id, 32 | time, 33 | deleted, 34 | kids, 35 | showComment, 36 | loadComments, 37 | fetchingReplies, 38 | fetchingRepliesFor, 39 | loadReplies, 40 | commentChain, 41 | reply, 42 | openSafari 43 | } = this.props; 44 | return ( 45 | 46 | {!deleted && 47 | } 66 | {deleted && 67 | 68 | deleted - {moment(time * 1000).fromNow()} 69 | } 70 | 71 | ); 72 | } 73 | } 74 | 75 | const styles = StyleSheet.create({ 76 | comment: { 77 | padding: 10, 78 | borderBottomWidth: 1, 79 | borderBottomColor: "#e2e2e2" 80 | }, 81 | reply: { 82 | paddingTop: 10, 83 | paddingBottom: 10, 84 | marginLeft: 5, 85 | paddingLeft: 10, 86 | borderLeftWidth: 1, 87 | borderLeftColor: "#d5d5d5" 88 | }, 89 | paddingTop: { 90 | paddingTop: 5 91 | } 92 | }); 93 | 94 | export default Comment; 95 | -------------------------------------------------------------------------------- /js/components/thread/comments.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, StyleSheet, Dimensions } from "react-native"; 3 | import List from "../generic/list"; 4 | import Comment from "./comment"; 5 | 6 | const Comments = ({ 7 | comments, 8 | refreshing, 9 | refreshThread, 10 | renderHeader, 11 | loadComments, 12 | fetchingReplies, 13 | fetchingRepliesFor, 14 | loadReplies, 15 | toggleComment, 16 | openSafari, 17 | height 18 | }) => { 19 | return ( 20 | 21 | refreshThread(comments.map(comment => comment.id))} 26 | items={comments} 27 | renderItem={({ item: comment }) => ( 28 | 37 | )} 38 | /> 39 | 40 | ); 41 | }; 42 | 43 | const styles = StyleSheet.create({ 44 | comments: {} 45 | }); 46 | 47 | export default Comments; 48 | -------------------------------------------------------------------------------- /js/components/thread/head.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Text from "../generic/text"; 4 | import Button from "../generic/button"; 5 | import { formatUrl, fromNow } from "../../helpers/story-helpers"; 6 | 7 | const Thread = ({ 8 | loading, 9 | title, 10 | score, 11 | time, 12 | url, 13 | descendants, 14 | saved, 15 | saveAction, 16 | story, 17 | openSafari 18 | }) => { 19 | return ( 20 | 50 | 51 | 52 | 53 | ); 54 | }; 55 | 56 | const styles = StyleSheet.create({ 57 | container: { 58 | padding: 10, 59 | borderBottomWidth: 1, 60 | borderBottomColor: "#e0e0e0" 61 | }, 62 | row: { 63 | flexDirection: "row", 64 | paddingTop: 10 65 | }, 66 | space: { 67 | justifyContent: "space-between" 68 | }, 69 | save: { 70 | backgroundColor: "#8e44ad", 71 | borderRadius: 10 72 | }, 73 | blue: { 74 | backgroundColor: "#2980b9" 75 | }, 76 | time: { 77 | paddingLeft: 10 78 | } 79 | }); 80 | 81 | export default Thread; 82 | -------------------------------------------------------------------------------- /js/components/thread/load-replies.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Text from "../generic/text"; 4 | import Button from "../generic/button"; 5 | 6 | const LoadReplies = props => { 7 | const { loadReplies, kids, isLoading, id, commentChain } = props; 8 | return ( 9 | 10 | 19 | 20 | ); 21 | }; 22 | 23 | const styles = StyleSheet.create({ 24 | container: { 25 | marginTop: 15 26 | }, 27 | button: { 28 | paddingTop: 5, 29 | paddingBottom: 5 30 | }, 31 | buttonText: { 32 | textAlign: "center", 33 | color: "#19467a" 34 | } 35 | }); 36 | 37 | export default LoadReplies; 38 | -------------------------------------------------------------------------------- /js/components/thread/no-comments.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import Text from "../generic/text"; 4 | 5 | const NoComments = () => ( 6 | 7 | No comments 8 | 9 | ); 10 | 11 | const styles = StyleSheet.create({ 12 | container: { 13 | justifyContent: "center", 14 | alignItems: "center", 15 | height: 300 16 | } 17 | }); 18 | 19 | export default NoComments; 20 | -------------------------------------------------------------------------------- /js/connected/thread.connected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View, ActivityIndicator } from "react-native"; 3 | import { connect } from "react-redux"; 4 | import { 5 | loadComments, 6 | refreshThread, 7 | loadReplies, 8 | toggleComment 9 | } from "../redux/action-creators/thread"; 10 | import Thread from "../components/thread"; 11 | 12 | class ThreadView extends Component { 13 | componentDidMount() { 14 | const { comments, story: { id, kids }, loadComments } = this.props; 15 | if (!comments.length || comments[0].parent !== id) { 16 | loadComments(kids); 17 | } 18 | } 19 | 20 | render() { 21 | return ; 22 | } 23 | } 24 | 25 | const mapStateToProps = ({ thread }) => ({ ...thread }); 26 | export default connect(mapStateToProps, { 27 | loadComments, 28 | refreshThread, 29 | loadReplies, 30 | toggleComment 31 | })(ThreadView); 32 | -------------------------------------------------------------------------------- /js/helpers/comment-helpers.js: -------------------------------------------------------------------------------- 1 | export const appendReplies = (comments, commentChain, replies) => { 2 | const updateComments = (comments, commentChainCopy) => { 3 | if (commentChainCopy.length === 1) { 4 | return comments.map(comment => { 5 | if (comment.id === commentChainCopy[0]) { 6 | return Object.assign(comment, { 7 | kids: replies.map(reply => 8 | Object.assign(reply, { 9 | showComment: true, 10 | commentChain: commentChain.concat([reply.id]) 11 | }) 12 | ) 13 | }); 14 | } else { 15 | return comment; 16 | } 17 | }); 18 | } else { 19 | return comments.map(comment => { 20 | if (comment.id === commentChainCopy[0]) { 21 | const kids = updateComments(comment.kids, commentChainCopy.slice(1)); 22 | return Object.assign(comment, { kids }); 23 | } else { 24 | return comment; 25 | } 26 | }); 27 | } 28 | }; 29 | 30 | return updateComments(comments, commentChain); 31 | }; 32 | 33 | export const toggleViewComment = (comments, commentChain = [], id) => { 34 | if (commentChain.length === 1) { 35 | return comments.map(comment => { 36 | return comment.id === id 37 | ? Object.assign(comment, { showComment: !comment.showComment }) 38 | : comment; 39 | }); 40 | } else { 41 | return comments.map(comment => { 42 | if (comment.id === commentChain[0]) { 43 | const showComment = toggleViewComment( 44 | comment.kids, 45 | commentChain.slice(1), 46 | id 47 | ); 48 | return Object.assign(comment, { showComment }); 49 | } else { 50 | return comment; 51 | } 52 | }); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /js/helpers/fetch-builder.js: -------------------------------------------------------------------------------- 1 | export const postJson = (url, commentIds) => 2 | fetch(url, { 3 | method: "POST", 4 | headers: { 5 | Accept: "application/json", 6 | "Content-Type": "application/json" 7 | }, 8 | body: JSON.stringify({ commentIds }) 9 | }).then(res => res.json()); 10 | -------------------------------------------------------------------------------- /js/helpers/get-title.js: -------------------------------------------------------------------------------- 1 | export const titles = { 2 | topstories: "Top Stories", 3 | beststories: "Best Stories", 4 | jobstories: "Jobs", 5 | askstories: "AskHN", 6 | showstories: "ShowHN" 7 | }; 8 | 9 | export default function getTitle(topicSelected) { 10 | return titles[topicSelected]; 11 | } 12 | -------------------------------------------------------------------------------- /js/helpers/story-helpers.js: -------------------------------------------------------------------------------- 1 | import moment from "moment"; 2 | 3 | export const formatUrl = url => { 4 | const trimUrl = (str, start) => { 5 | return str.split("").slice(start, str.length).join("").split("/")[0]; 6 | }; 7 | 8 | if (url) { 9 | return url.match("http://") ? trimUrl(url, 7) : trimUrl(url, 8); 10 | } 11 | 12 | return null; 13 | }; 14 | 15 | export const fromNow = time => { 16 | return moment(time * 1000).fromNow(); 17 | }; 18 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider } from "react-redux"; 3 | import store from "./store"; 4 | import App from "./app.container"; 5 | 6 | const HackerNews = () => ( 7 | 8 | 9 | 10 | ); 11 | 12 | export default HackerNews; 13 | -------------------------------------------------------------------------------- /js/layout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Dimensions, View, Text, StyleSheet } from "react-native"; 3 | import StatusBar from "./components/status-bar"; 4 | import TabLayout from "./components/tab-layout"; 5 | 6 | const window = Dimensions.get("window"); 7 | const HEIGHT = window.height; 8 | const WIDTH = window.width; 9 | 10 | const Layout = props => { 11 | const { 12 | title, 13 | navigator, 14 | isHome, 15 | component: NewView, 16 | linkProps, 17 | openSafari, 18 | saveStory, 19 | unSaveStory 20 | } = props; 21 | 22 | return ( 23 | 24 | 30 | {isHome && 31 | } 32 | {!isHome && 33 | } 39 | 40 | ); 41 | }; 42 | 43 | const styles = StyleSheet.create({ 44 | layout: { 45 | height: HEIGHT, 46 | width: WIDTH 47 | }, 48 | statusBar: { 49 | alignSelf: "flex-start" 50 | }, 51 | tabLayout: { 52 | alignSelf: "flex-end" 53 | } 54 | }); 55 | 56 | export default Layout; 57 | -------------------------------------------------------------------------------- /js/redux/action-creators/fetch-builder.js: -------------------------------------------------------------------------------- 1 | import { ROOT_URL } from "../constants"; 2 | 3 | export const getJson = (endpoint, topic, query = "") => { 4 | let url = `${ROOT_URL}/${endpoint}`; 5 | url += topic ? `/${topic}` : ""; 6 | url += query ? `?${query}` : ""; 7 | 8 | return fetch(url, { 9 | method: "GET", 10 | headers: { 11 | Accept: "application/json", 12 | "Content-Type": "application/json" 13 | } 14 | }) 15 | .then(res => res.json()) 16 | .then(storyObj => { 17 | // TODO: only send stories when being called by changeTopic 18 | return storyObj; 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /js/redux/action-creators/settings.js: -------------------------------------------------------------------------------- 1 | import { AsyncStorage } from "react-native"; 2 | import { createAction } from "redux-actions"; 3 | import { 4 | LOAD_SETTINGS_SUCCESS, 5 | LOAD_SETTINGS_ERROR, 6 | CHANGE_VIEW, 7 | CHANGE_TOPIC_REQUEST, 8 | CHANGE_TOPIC_SUCCESS, 9 | CHANGE_TOPIC_ERROR 10 | } from "../constants"; 11 | import { getJson } from "./fetch-builder"; 12 | 13 | const onLoadSettingsSuccess = createAction(LOAD_SETTINGS_SUCCESS); 14 | const onLoadSettingsError = createAction(LOAD_SETTINGS_ERROR); 15 | const onChangeView = createAction(CHANGE_VIEW); 16 | 17 | const onChangeTopicRequest = createAction(CHANGE_TOPIC_REQUEST); 18 | const onChangeTopicSuccess = createAction(CHANGE_TOPIC_SUCCESS); 19 | const onChangeTopicError = createAction(CHANGE_TOPIC_ERROR); 20 | 21 | export const fetchInitialData = selectedTopic => { 22 | return dispatch => { 23 | AsyncStorage.multiGet(["settings", "savedStories"]) 24 | .then(res => { 25 | const settingsRes = res[0][1]; 26 | const settings = (settingsRes && JSON.parse(settingsRes)) || {}; 27 | const savedStoriesRes = res[1][1]; 28 | const savedStoryIds = (savedStoriesRes && 29 | JSON.parse(savedStoriesRes)) || []; 30 | 31 | let query = ""; 32 | if (savedStoryIds.length) { 33 | query += `savedStories=${savedStoryIds.join(",")}`; 34 | } 35 | 36 | getJson("stories", selectedTopic, query) 37 | .then((initialStories = []) => { 38 | dispatch(onLoadSettingsSuccess({ settings, initialStories })); 39 | }) 40 | .catch(err => { 41 | dispatch(onLoadSettingsError(err)); 42 | }); 43 | }) 44 | .catch(err => { 45 | dispatch(onLoadSettingsError(err)); 46 | }); 47 | }; 48 | }; 49 | 50 | export function changeView() { 51 | return dispatch => dispatch(onChangeView()); 52 | } 53 | 54 | export function changeTopic(newTopic) { 55 | return dispatch => { 56 | dispatch(onChangeTopicRequest({ newTopic })); 57 | 58 | getJson("stories", newTopic) 59 | .then(({ stories }) => { 60 | dispatch(onChangeTopicSuccess({ stories })); 61 | }) 62 | .catch(err => dispatch(onChangeTopicError(err))); 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /js/redux/action-creators/stories.js: -------------------------------------------------------------------------------- 1 | import { AsyncStorage } from "react-native"; 2 | import { createAction } from "redux-actions"; 3 | import { 4 | SAVE_STORY_SUCCESS, 5 | SAVE_STORY_ERROR, 6 | UN_SAVE_STORY_SUCCESS, 7 | UN_SAVE_STORY_ERROR, 8 | REFRESH_STORIES_REQUEST, 9 | REFRESH_STORIES_SUCCESS, 10 | REFRESH_STORIES_ERROR, 11 | REFRESH_SAVED_STORIES_REQUEST, 12 | REFRESH_SAVED_STORIES_SUCCESS, 13 | REFRESH_SAVED_STORIES_ERROR 14 | } from "../constants"; 15 | import { getJson } from "./fetch-builder"; 16 | 17 | const onSaveStorySuccess = createAction(SAVE_STORY_SUCCESS); 18 | const onSaveStoryError = createAction(SAVE_STORY_ERROR); 19 | const onUnSaveStorySuccess = createAction(UN_SAVE_STORY_SUCCESS); 20 | const onUnSaveStoryError = createAction(UN_SAVE_STORY_ERROR); 21 | const onRefreshStoriesRequest = createAction(REFRESH_STORIES_REQUEST); 22 | const onRefreshStoriesSuccess = createAction(REFRESH_STORIES_SUCCESS); 23 | const onRefreshStoriesError = createAction(REFRESH_STORIES_ERROR); 24 | const onRefreshSavedStoriesRequest = createAction( 25 | REFRESH_SAVED_STORIES_REQUEST 26 | ); 27 | const onRefreshSavedStoriesSuccess = createAction( 28 | REFRESH_SAVED_STORIES_SUCCESS 29 | ); 30 | const onRefreshSavedStoriesError = createAction(REFRESH_SAVED_STORIES_ERROR); 31 | 32 | const getSavedStories = () => { 33 | return AsyncStorage.getItem("savedStories").then(savedStoriesRes => { 34 | const savedStories = (savedStoriesRes && JSON.parse(savedStoriesRes)) || []; 35 | return savedStories; 36 | }); 37 | }; 38 | 39 | const setSavedStories = (dispatch, stories, story, unSaving) => { 40 | const toggleSaveAction = unSaving ? onUnSaveStorySuccess : onSaveStorySuccess; 41 | const toggleSaveActionErr = unSaving ? onUnSaveStoryError : onSaveStoryError; 42 | AsyncStorage.setItem("savedStories", JSON.stringify(stories)) 43 | .then(() => dispatch(toggleSaveAction({ storyIds: stories, story }))) 44 | .catch(err => dispatch(toggleSaveActionErr(err))); 45 | }; 46 | 47 | export function saveStory(story) { 48 | return dispatch => { 49 | getSavedStories().then(stories => { 50 | if (!stories.length) { 51 | return setSavedStories(dispatch, [story.id], story); 52 | } 53 | 54 | const newStories = stories.slice(); 55 | newStories.unshift(story.id); 56 | setSavedStories(dispatch, newStories, story); 57 | }); 58 | }; 59 | } 60 | 61 | export function unSaveStory(story) { 62 | return dispatch => { 63 | getSavedStories().then(stories => { 64 | const newStories = stories.filter(storyId => { 65 | return storyId !== story.id; 66 | }); 67 | return setSavedStories(dispatch, newStories, story, true); 68 | }); 69 | }; 70 | } 71 | 72 | export function refreshStories(endpoint) { 73 | return (dispatch, getState) => { 74 | const state = getState(); 75 | // hmm... 76 | const { savedStories: { savedStories } } = state; 77 | const slimSavedStories = savedStories.map(s => s.id); 78 | 79 | dispatch(onRefreshStoriesRequest()); 80 | 81 | getJson("stories", endpoint) 82 | .then(({ stories }) => { 83 | const newStories = stories.map( 84 | story => 85 | slimSavedStories.indexOf(story.id) === -1 86 | ? story 87 | : Object.assign(story, { saved: true }) 88 | ); 89 | dispatch(onRefreshStoriesSuccess({ newStories })); 90 | }) 91 | .catch(err => dispatch(onRefreshStoriesError(err))); 92 | }; 93 | } 94 | 95 | export function refreshSavedStories(storyIds) { 96 | const query = `stories=${storyIds.join(",")}`; 97 | 98 | return dispatch => { 99 | dispatch(onRefreshSavedStoriesRequest()); 100 | 101 | getJson("saved", null, query) 102 | .then(({ stories }) => { 103 | dispatch(onRefreshSavedStoriesSuccess({ stories })); 104 | }) 105 | .catch(err => dispatch(onRefreshSavedStoriesError(err))); 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /js/redux/action-creators/thread.js: -------------------------------------------------------------------------------- 1 | import { AsyncStorage } from "react-native"; 2 | import { createAction } from "redux-actions"; 3 | import { 4 | FETCH_COMMENTS_REQUEST, 5 | FETCH_COMMENTS_SUCCESS, 6 | FETCH_COMMENTS_ERROR, 7 | REFRESH_THREAD_REQUEST, 8 | REFRESH_THREAD_SUCCESS, 9 | REFRESH_THREAD_ERROR, 10 | FETCH_REPLIES_REQUEST, 11 | FETCH_REPLIES_SUCCESS, 12 | FETCH_REPLIES_ERROR, 13 | TOGGLE_COMMENT 14 | } from "../constants"; 15 | import { getJson } from "./fetch-builder"; 16 | 17 | const onFetchCommentsRequest = createAction(FETCH_COMMENTS_REQUEST); 18 | const onFetchCommentsSuccess = createAction(FETCH_COMMENTS_SUCCESS); 19 | const onFetchCommentsError = createAction(FETCH_COMMENTS_ERROR); 20 | const onRefreshThreadRequest = createAction(REFRESH_THREAD_REQUEST); 21 | const onRefreshThreadSuccess = createAction(REFRESH_THREAD_SUCCESS); 22 | const onRefreshThreadError = createAction(REFRESH_THREAD_ERROR); 23 | const onFetchRepliesRequest = createAction(FETCH_REPLIES_REQUEST); 24 | const onFetchRepliesSuccess = createAction(FETCH_REPLIES_SUCCESS); 25 | const onFetchRepliesError = createAction(FETCH_REPLIES_ERROR); 26 | const onToggleComment = createAction(TOGGLE_COMMENT); 27 | 28 | export function loadComments(commentIds = [], head) { 29 | return dispatch => { 30 | if (!commentIds.length) { 31 | return dispatch(onFetchCommentsSuccess({ comments: [] })); 32 | } 33 | 34 | const query = `comments=${commentIds.join(",")}`; 35 | 36 | dispatch(onFetchCommentsRequest({ head })); 37 | getJson("comments", null, query) 38 | .then(comments => dispatch(onFetchCommentsSuccess(comments))) 39 | .catch(err => dispatch(onFetchCommentsError({ err }))); 40 | }; 41 | } 42 | 43 | export function refreshThread(commentIds, head) { 44 | const query = `comments=${commentIds.join(",")}`; 45 | 46 | return dispatch => { 47 | dispatch(onRefreshThreadRequest({ head })); 48 | 49 | getJson("comments", null, query) 50 | .then(({ comments }) => { 51 | dispatch(onRefreshThreadSuccess({ comments })); 52 | }) 53 | .catch(err => dispatch(onRefreshThreadError(err))); 54 | }; 55 | } 56 | 57 | export function loadReplies(opId, commentChain, commentIds) { 58 | const query = `comments=${commentIds.join(",")}`; 59 | 60 | return dispatch => { 61 | dispatch(onFetchRepliesRequest({ opId })); 62 | 63 | getJson("comments", null, query) 64 | .then(({ comments }) => { 65 | dispatch(onFetchRepliesSuccess({ comments, commentChain, opId })); 66 | }) 67 | .catch(err => dispatch(onFetchRepliesError(err))); 68 | }; 69 | } 70 | 71 | export function toggleComment(id, commentChain) { 72 | return dispatch => { 73 | dispatch(onToggleComment({ id, commentChain })); 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /js/redux/constants.js: -------------------------------------------------------------------------------- 1 | export const ROOT_URL = "https://hackernews-server-igvdaqfamu.now.sh"; 2 | // export const ROOT_URL = 'http://localhost:5000' 3 | 4 | export const LOAD_SETTINGS_SUCCESS = "LOAD_SETTINGS_SUCCESS"; 5 | export const LOAD_SETTINGS_ERROR = "LOAD_SETTINGS_ERROR"; 6 | 7 | export const CHANGE_VIEW = "CHANGE_VIEW"; 8 | 9 | export const FETCH_TOPIC_STORIES_REQUEST = "FETCH_TOPIC_STORIES_REQUEST"; 10 | export const FETCH_TOPIC_STORIES_SUCCESS = "FETCH_TOPIC_STORIES_SUCCESS"; 11 | export const FETCH_TOPIC_STORIES_ERROR = "FETCH_TOPIC_STORIES_ERROR"; 12 | 13 | export const FETCH_SAVED_STORIES_REQUEST = "FETCH_SAVED_STORIES_REQUEST"; 14 | export const FETCH_SAVED_STORIES_SUCCESS = "FETCH_SAVED_STORIES_SUCCESS"; 15 | export const FETCH_SAVED_STORIES_ERROR = "FETCH_SAVED_STORIES_ERROR"; 16 | 17 | export const SAVE_STORY_SUCCESS = "SAVE_STORY_SUCCESS"; 18 | export const SAVE_STORY_ERROR = "SAVE_STORY_ERROR"; 19 | 20 | export const UN_SAVE_STORY_SUCCESS = "UN_SAVE_STORY_SUCCESS"; 21 | export const UN_SAVE_STORY_ERROR = "UN_SAVE_STORY_ERROR"; 22 | 23 | export const FETCH_COMMENTS_REQUEST = "FETCH_COMMENTS_REQUEST"; 24 | export const FETCH_COMMENTS_SUCCESS = "FETCH_COMMENTS_SUCCESS"; 25 | export const FETCH_COMMENTS_ERROR = "FETCH_COMMENTS_ERROR"; 26 | 27 | export const CHANGE_TOPIC_REQUEST = "CHANGE_TOPIC_REQUEST"; 28 | export const CHANGE_TOPIC_SUCCESS = "CHANGE_TOPIC_SUCCESS"; 29 | export const CHANGE_TOPIC_ERROR = "CHANGE_TOPIC_ERROR"; 30 | 31 | export const REFRESH_STORIES_REQUEST = "REFRESH_STORIES_REQUEST"; 32 | export const REFRESH_STORIES_SUCCESS = "REFRESH_STORIES_SUCCESS"; 33 | export const REFRESH_STORIES_ERROR = "REFRESH_STORIES_ERROR"; 34 | 35 | export const REFRESH_SAVED_STORIES_REQUEST = "REFRESH_SAVED_STORIES_REQUEST"; 36 | export const REFRESH_SAVED_STORIES_SUCCESS = "REFRESH_SAVED_STORIES_SUCCESS"; 37 | export const REFRESH_SAVED_STORIES_ERROR = "REFRESH_SAVED_STORIES_ERROR"; 38 | 39 | export const REFRESH_THREAD_REQUEST = "REFRESH_THREAD_REQUEST"; 40 | export const REFRESH_THREAD_SUCCESS = "REFRESH_THREAD_SUCCESS"; 41 | export const REFRESH_THREAD_ERROR = "REFRESH_THREAD_ERROR"; 42 | 43 | export const FETCH_REPLIES_REQUEST = "FETCH_REPLIES_REQUEST"; 44 | export const FETCH_REPLIES_SUCCESS = "FETCH_REPLIES_SUCCESS"; 45 | export const FETCH_REPLIES_ERROR = "FETCH_REPLIES_ERROR"; 46 | 47 | export const TOGGLE_COMMENT = "TOGGLE_COMMENT"; 48 | -------------------------------------------------------------------------------- /js/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import settings from "./settings"; 3 | import stories from "./stories"; 4 | import savedStories from "./saved-stories"; 5 | import thread from "./thread"; 6 | 7 | export default combineReducers({ 8 | settings, 9 | stories, 10 | savedStories, 11 | thread 12 | }); 13 | -------------------------------------------------------------------------------- /js/redux/reducers/saved-stories.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from "redux-actions"; 2 | import { 3 | LOAD_SETTINGS_SUCCESS, 4 | FETCH_SAVED_STORIES_REQUEST, 5 | FETCH_SAVED_STORIES_SUCCESS, 6 | FETCH_SAVED_STORIES_ERROR, 7 | SAVE_STORY_SUCCESS, 8 | UN_SAVE_STORY_SUCCESS, 9 | REFRESH_SAVED_STORIES_REQUEST, 10 | REFRESH_SAVED_STORIES_SUCCESS, 11 | REFRESH_SAVED_STORIES_ERROR 12 | } from "../constants"; 13 | 14 | const initialState = { 15 | loading: false, 16 | refreshing: false, 17 | error: null, 18 | savedStories: [] 19 | }; 20 | 21 | export default handleActions( 22 | { 23 | [LOAD_SETTINGS_SUCCESS]: (state, { payload }) => { 24 | const savedStories = payload.initialStories.savedStories || []; 25 | const newStories = savedStories.map(story => 26 | Object.assign(story, { saved: true }) 27 | ); 28 | 29 | return { 30 | ...state, 31 | loading: false, 32 | savedStories: newStories 33 | }; 34 | }, 35 | [FETCH_SAVED_STORIES_REQUEST]: (state, { payload }) => ({ 36 | ...state, 37 | error: null, 38 | loading: true 39 | }), 40 | [FETCH_SAVED_STORIES_SUCCESS]: (state, { payload }) => { 41 | return { 42 | ...state, 43 | loading: false, 44 | savedStories: payload.stories.map(story => 45 | Object.assign(story, { saved: true }) 46 | ) 47 | }; 48 | }, 49 | [FETCH_SAVED_STORIES_ERROR]: (state, { payload }) => { 50 | return { 51 | ...state, 52 | loading: false, 53 | error: payload.error 54 | }; 55 | }, 56 | [SAVE_STORY_SUCCESS]: (state, { payload: { story } }) => { 57 | let newStories = state.savedStories.slice(); 58 | newStories.unshift(story); 59 | 60 | return { 61 | ...state, 62 | savedStories: newStories 63 | }; 64 | }, 65 | [UN_SAVE_STORY_SUCCESS]: (state, { payload: { story: activeStory } }) => { 66 | const newStories = state.savedStories.filter(story => { 67 | return story.id !== activeStory.id; 68 | }); 69 | return { 70 | ...state, 71 | savedStories: newStories 72 | }; 73 | }, 74 | [REFRESH_SAVED_STORIES_REQUEST]: (state, { payload }) => ({ 75 | ...state, 76 | loading: false, 77 | refreshing: true 78 | }), 79 | [REFRESH_SAVED_STORIES_SUCCESS]: (state, { payload: { stories } }) => ({ 80 | ...state, 81 | refreshing: false, 82 | stories 83 | }), 84 | [REFRESH_SAVED_STORIES_ERROR]: (state, { payload }) => ({ 85 | ...state, 86 | loading: false, 87 | error: payload 88 | }) 89 | }, 90 | initialState 91 | ); 92 | -------------------------------------------------------------------------------- /js/redux/reducers/settings.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from "redux-actions"; 2 | import { 3 | LOAD_SETTINGS_SUCCESS, 4 | LOAD_SETTINGS_ERROR, 5 | CHANGE_VIEW, 6 | SAVE_STORY_SUCCESS, 7 | UN_SAVE_STORY_SUCCESS, 8 | CHANGE_TOPIC_REQUEST 9 | } from "../constants"; 10 | import getTitle from "../../helpers/get-title"; 11 | 12 | const initialState = { 13 | loading: true, 14 | error: false, 15 | viewingStories: true, 16 | title: "Top Stories", 17 | topics: { 18 | currentlySelected: "topstories", 19 | available: [ 20 | "topstories", 21 | "askstories", 22 | "showstories", 23 | "beststories", 24 | "jobstories" 25 | ] 26 | }, 27 | savedStoryIds: [], 28 | settings: { 29 | darkMode: { 30 | id: "darkmode", 31 | name: "Dark mode", 32 | active: false 33 | } 34 | } 35 | }; 36 | 37 | export default handleActions( 38 | { 39 | [LOAD_SETTINGS_SUCCESS]: ( 40 | state, 41 | { payload: { settings, savedStoryIds = [] } } 42 | ) => { 43 | return { 44 | ...state, 45 | loading: false, 46 | settings: settings, 47 | savedStoryIds: savedStoryIds 48 | }; 49 | }, 50 | [LOAD_SETTINGS_ERROR]: (state, { payload }) => ({ 51 | ...state, 52 | loading: false, 53 | error: payload 54 | }), 55 | [CHANGE_VIEW]: (state, { payload }) => { 56 | const { viewingStories, topics: { currentlySelected } } = state; 57 | const newViewingStoriesBool = !state.viewingStories; 58 | 59 | const title = newViewingStoriesBool 60 | ? getTitle(currentlySelected) 61 | : "Read it later"; 62 | return { 63 | ...state, 64 | viewingStories: !state.viewingStories, 65 | title 66 | }; 67 | }, 68 | [SAVE_STORY_SUCCESS]: (state, { payload }) => { 69 | return { 70 | ...state, 71 | savedStoryIds: payload.savedStoryIds 72 | }; 73 | }, 74 | [UN_SAVE_STORY_SUCCESS]: (state, { payload }) => { 75 | return { 76 | ...state, 77 | savedStoryIds: payload.savedStoryIds 78 | }; 79 | }, 80 | [CHANGE_TOPIC_REQUEST]: (state, { payload: { newTopic } }) => ({ 81 | ...state, 82 | title: getTitle(newTopic), 83 | topics: { available: state.topics.available, currentlySelected: newTopic } 84 | }) 85 | }, 86 | initialState 87 | ); 88 | -------------------------------------------------------------------------------- /js/redux/reducers/stories.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from "redux-actions"; 2 | import { 3 | LOAD_SETTINGS_SUCCESS, 4 | FETCH_TOPIC_STORIES_REQUEST, 5 | FETCH_TOPIC_STORIES_SUCCESS, 6 | FETCH_TOPIC_STORIES_ERROR, 7 | SAVE_STORY_SUCCESS, 8 | UN_SAVE_STORY_SUCCESS, 9 | CHANGE_TOPIC_REQUEST, 10 | CHANGE_TOPIC_SUCCESS, 11 | CHANGE_TOPIC_ERROR, 12 | REFRESH_STORIES_REQUEST, 13 | REFRESH_STORIES_SUCCESS, 14 | REFRESH_STORIES_ERROR 15 | } from "../constants"; 16 | 17 | const initialState = { 18 | loading: true, 19 | refreshing: false, 20 | error: null, 21 | stories: [] 22 | }; 23 | 24 | export default handleActions( 25 | { 26 | [LOAD_SETTINGS_SUCCESS]: (state, { payload }) => { 27 | const stories = payload.initialStories.stories || []; 28 | const savedStories = payload.initialStories.savedStories || []; 29 | 30 | const isSavedStory = storyIdInQuestion => { 31 | for (var i = 0; i < savedStories.length; i++) { 32 | if (savedStories[i].id === storyIdInQuestion) { 33 | return true; 34 | } 35 | } 36 | }; 37 | 38 | const storiesWithAttrs = stories.map(story => { 39 | if (isSavedStory(story.id)) { 40 | return Object.assign(story, { saved: true }); 41 | } else { 42 | return story; 43 | } 44 | }); 45 | 46 | return { 47 | ...state, 48 | loading: false, 49 | stories: storiesWithAttrs 50 | }; 51 | }, 52 | [FETCH_TOPIC_STORIES_REQUEST]: (state, { payload }) => ({ 53 | ...state, 54 | error: null 55 | }), 56 | [FETCH_TOPIC_STORIES_SUCCESS]: (state, { payload }) => { 57 | return { 58 | ...state, 59 | loading: false, 60 | stories: payload.stories.map(story => { 61 | if (payload.savedStoryIds.indexOf(story.id) !== -1) { 62 | return Object.assign(story, { saved: true }); 63 | } 64 | return story; 65 | }) 66 | }; 67 | }, 68 | [FETCH_TOPIC_STORIES_ERROR]: (state, { payload }) => { 69 | return { 70 | ...state, 71 | loading: false, 72 | error: payload.err 73 | }; 74 | }, 75 | [SAVE_STORY_SUCCESS]: (state, { payload }) => { 76 | // load new storeis with correct "saved" attr 77 | const savedStories = payload.savedStoryIds; 78 | const newStories = state.stories.map(story => { 79 | return story.id === payload.story.id 80 | ? Object.assign(story, { saved: true }) 81 | : story; 82 | }); 83 | return { 84 | ...state, 85 | stories: newStories 86 | }; 87 | }, 88 | [UN_SAVE_STORY_SUCCESS]: (state, { payload }) => { 89 | const savedStories = payload.savedStoryIds; 90 | const newStories = state.stories.map(story => { 91 | return story.id === payload.story.id 92 | ? Object.assign(story, { saved: false }) 93 | : story; 94 | }); 95 | return { 96 | ...state, 97 | stories: newStories 98 | }; 99 | }, 100 | [CHANGE_TOPIC_REQUEST]: state => ({ 101 | ...state, 102 | loading: true, 103 | stories: [] 104 | }), 105 | [CHANGE_TOPIC_SUCCESS]: (state, { payload: { stories } }) => ({ 106 | ...state, 107 | loading: false, 108 | stories 109 | }), 110 | [CHANGE_TOPIC_ERROR]: (state, { payload }) => ({ 111 | ...state, 112 | loading: false, 113 | error: payload 114 | }), 115 | [REFRESH_STORIES_REQUEST]: (state, { payload }) => ({ 116 | ...state, 117 | error: null, 118 | loading: false, 119 | refreshing: true 120 | }), 121 | [REFRESH_STORIES_SUCCESS]: (state, { payload: { newStories } }) => { 122 | return { 123 | ...state, 124 | refreshing: false, 125 | stories: newStories 126 | }; 127 | }, 128 | [REFRESH_STORIES_ERROR]: (state, { payload }) => ({ 129 | ...state, 130 | loading: false, 131 | error: payload 132 | }) 133 | }, 134 | initialState 135 | ); 136 | -------------------------------------------------------------------------------- /js/redux/reducers/thread.js: -------------------------------------------------------------------------------- 1 | import { handleActions } from "redux-actions"; 2 | import { 3 | FETCH_COMMENTS_REQUEST, 4 | FETCH_COMMENTS_SUCCESS, 5 | FETCH_COMMENTS_ERROR, 6 | REFRESH_THREAD_REQUEST, 7 | REFRESH_THREAD_SUCCESS, 8 | REFRESH_THREAD_ERROR, 9 | FETCH_REPLIES_REQUEST, 10 | FETCH_REPLIES_SUCCESS, 11 | FETCH_REPLIES_ERROR, 12 | TOGGLE_COMMENT 13 | } from "../constants"; 14 | import { 15 | toggleViewComment, 16 | appendReplies 17 | } from "../../helpers/comment-helpers"; 18 | 19 | const initialState = { 20 | loading: true, 21 | refreshing: false, 22 | error: null, 23 | fetchingReplies: false, 24 | fetchingRepliesFor: null, 25 | comments: [] 26 | }; 27 | 28 | export default handleActions( 29 | { 30 | [FETCH_COMMENTS_REQUEST]: state => { 31 | return { 32 | ...state, 33 | error: null, 34 | comments: [] 35 | }; 36 | }, 37 | [FETCH_COMMENTS_SUCCESS]: (state, { payload: { comments } }) => { 38 | return { 39 | ...state, 40 | error: null, 41 | loading: false, 42 | comments: comments.map(comment => 43 | Object.assign({}, comment, { 44 | commentChain: [comment.id], 45 | showComment: true 46 | }) 47 | ) 48 | }; 49 | }, 50 | [FETCH_COMMENTS_ERROR]: (state, { payload }) => { 51 | return { 52 | ...state, 53 | error: payload.err 54 | }; 55 | }, 56 | [REFRESH_THREAD_REQUEST]: (state, { payload }) => ({ 57 | ...state, 58 | loading: false, 59 | refreshing: true 60 | }), 61 | [REFRESH_THREAD_SUCCESS]: (state, { payload: { comments } }) => ({ 62 | ...state, 63 | refreshing: false, 64 | comments 65 | }), 66 | [REFRESH_THREAD_ERROR]: (state, { payload }) => ({ 67 | ...state, 68 | loading: false, 69 | error: payload 70 | }), 71 | [FETCH_REPLIES_REQUEST]: (state, { payload: { opId } }) => ({ 72 | ...state, 73 | fetchingReplies: true, 74 | fetchingRepliesFor: opId 75 | }), 76 | [FETCH_REPLIES_SUCCESS]: ( 77 | state, 78 | { payload: { comments: replies, commentChain } } 79 | ) => { 80 | return { 81 | ...state, 82 | loading: false, 83 | fetchingReplies: false, 84 | fetchingRepliesFor: null, 85 | comments: appendReplies(state.comments, commentChain, replies) 86 | }; 87 | }, 88 | [FETCH_REPLIES_ERROR]: (state, { payload }) => ({ 89 | ...state, 90 | loading: false, 91 | error: payload 92 | }), 93 | [TOGGLE_COMMENT]: (state, { payload: { id, commentChain } }) => ({ 94 | ...state, 95 | comments: toggleViewComment(state.comments, commentChain, id) 96 | }) 97 | }, 98 | initialState 99 | ); 100 | -------------------------------------------------------------------------------- /js/store.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, createStore } from "redux"; 2 | import thunk from "redux-thunk"; 3 | import promise from "redux-promise"; 4 | import { createLogger } from "redux-logger"; 5 | import reducers from "./redux/reducers"; 6 | 7 | const isDev = __DEV__; // eslint-disable-line no-undef 8 | const logger = createLogger(); 9 | 10 | const middleware = isDev 11 | ? applyMiddleware(thunk, promise, logger) 12 | : applyMiddleware(thunk); 13 | 14 | const store = createStore(reducers, middleware); 15 | 16 | export default store; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hnews", 3 | "version": "2.0.2", 4 | "scripts": { 5 | "start": "node node_modules/react-native/local-cli/cli.js start", 6 | "lint": "eslint ./src/components/**.js", 7 | "precommit": "lint-staged" 8 | }, 9 | "lint-staged": { 10 | "js/**/*.js": [ 11 | "prettier --write", 12 | "git add" 13 | ] 14 | }, 15 | "dependencies": { 16 | "moment": "^2.18.1", 17 | "prop-types": "^15.5.8", 18 | "react": "16.0.0-alpha.6", 19 | "react-native": "0.43", 20 | "react-native-collapsible": "^0.8.0", 21 | "react-native-deprecated-custom-components": "^0.1.0", 22 | "react-native-htmlview": "^0.9.0", 23 | "react-native-safari-view": "^2.0.0", 24 | "react-native-tab-view": "0.0.58", 25 | "react-native-vector-icons": "^4.4.0", 26 | "react-redux": "^5.0.3", 27 | "redux": "^3.6.0", 28 | "redux-actions": "^2.0.1", 29 | "redux-logger": "^3.0.0", 30 | "redux-promise": "^0.5.3", 31 | "redux-thunk": "^2.2.0" 32 | }, 33 | "devDependencies": { 34 | "babel-eslint": "^6.1.0", 35 | "eslint": "2.7.0", 36 | "eslint-plugin-react": "4.3.0", 37 | "eslint-plugin-react-native": "1.0.0", 38 | "husky": "^0.13.3", 39 | "lint-staged": "^3.4.2", 40 | "prettier": "^1.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pre-.commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #husky 0.13.3 3 | 4 | command_exists () { 5 | command -v "$1" >/dev/null 2>&1 6 | } 7 | 8 | load_nvm () { 9 | export $1=$2 10 | [ -s "$2/nvm.sh" ] && . $2/nvm.sh 11 | command_exists nvm && [ -f .nvmrc ] && nvm use 12 | } 13 | 14 | has_hook_script () { 15 | [ -f package.json ] && cat package.json | grep -q "\"$1\"[[:space:]]*:" 16 | } 17 | 18 | cd . 19 | 20 | has_hook_script precommit || exit 0 21 | 22 | export PATH=$PATH:/usr/local/bin:/usr/local 23 | load_nvm BREW_NVM_DIR /usr/local/opt/nvm 24 | 25 | load_nvm NVM_DIR /Users/seanyesmunt/.nvm 26 | 27 | command_exists npm || { 28 | echo >&2 "> husky - Can't find npm in PATH. Skipping precommit script in package.json" 29 | exit 0 30 | } 31 | 32 | echo 33 | echo "> husky - npm run -s precommit" 34 | echo 35 | 36 | export GIT_PARAMS="$*" 37 | npm run -s precommit || { 38 | echo 39 | echo "> husky - pre-commit hook failed (add --no-verify to bypass)" 40 | echo "> husky - to debug, use 'npm run precommit'" 41 | exit 1 42 | } 43 | --------------------------------------------------------------------------------