├── .buckconfig ├── .eslintrc.js ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .watchmanconfig ├── 1.gif ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── App.js ├── CLEAR_CACHE.md ├── FURTURE.md ├── LICENSE ├── README.md ├── README_EN.md ├── __tests__ └── App-test.js ├── android ├── app │ ├── _BUCK │ ├── build.gradle │ ├── build_defs.bzl │ ├── debug.keystore │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── fonts │ │ │ ├── AntDesign.ttf │ │ │ ├── Entypo.ttf │ │ │ ├── EvilIcons.ttf │ │ │ ├── Feather.ttf │ │ │ ├── FontAwesome.ttf │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ ├── Fontisto.ttf │ │ │ ├── Foundation.ttf │ │ │ ├── Ionicons.ttf │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ ├── MaterialIcons.ttf │ │ │ ├── Octicons.ttf │ │ │ ├── SimpleLineIcons.ttf │ │ │ └── Zocial.ttf │ │ ├── java │ │ └── com │ │ │ └── gsygithubapp │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gsygithubapp-debug.jks └── settings.gradle ├── app.json ├── app ├── components │ ├── AboutPage.js │ ├── CodeDetailPage.js │ ├── DynamicPage.js │ ├── IssueDetailPage.js │ ├── ListPage.js │ ├── LoginPage.js │ ├── LoginWebPage.js │ ├── MyPage.js │ ├── NotifyPage.js │ ├── PersonInfoPage.js │ ├── PersonPage.js │ ├── PhotoPage.js │ ├── PushDetailPage.js │ ├── RecommendPage.js │ ├── ReleasePage.js │ ├── RepositoryDetailActivityPage.js │ ├── RepositoryDetailFilePage.js │ ├── RepositoryDetailPage.js │ ├── RepositoryIssueListPage.js │ ├── SearchPage.js │ ├── SettingPage.js │ ├── TrendPage.js │ ├── WebPage.js │ ├── WelcomePage.js │ ├── common │ │ ├── CommonBottomBar.js │ │ ├── CommonConfirmModal.js │ │ ├── CommonHtmlView.js │ │ ├── CommonIconButton.js │ │ ├── CommonInputBar.js │ │ ├── CommonNameValueItem.js │ │ ├── CommonOptionModal.js │ │ ├── CommonRowItem.js │ │ ├── CommonTextInputModal.js │ │ ├── LoadingModal.js │ │ ├── LogHoc.js │ │ ├── ModalBox.js │ │ ├── ModalDropdown.js │ │ └── ToastProxy.js │ └── widget │ │ ├── BasePersonPage.js │ │ ├── BottomPopmenuItem.js │ │ ├── CodeFileItem.js │ │ ├── CustomBackButton.js │ │ ├── CustomDrawerButton.js │ │ ├── CustomSearchButton.js │ │ ├── CustomWebComponent.js │ │ ├── EventItem.js │ │ ├── IconTextAutoLinkItem.js │ │ ├── IconTextItem.js │ │ ├── IssueHead.js │ │ ├── IssueItem.js │ │ ├── OrgItemBar.js │ │ ├── PullLoadMoreListView.js │ │ ├── PushDetailHeader.js │ │ ├── ReleaseItem.js │ │ ├── RepositoryHeader.js │ │ ├── RepositoryItem.js │ │ ├── RepositoryPulseItem.js │ │ ├── SearchDrawerFilter.js │ │ ├── SearchFilterSelectList.js │ │ ├── TabIcon.js │ │ ├── TagGroup.js │ │ ├── TimeText.js │ │ ├── TrendPickerItem.js │ │ ├── UserHeadItem.js │ │ ├── UserImage.js │ │ └── UserItem.js ├── config │ └── index.js ├── dao │ ├── db │ │ └── index.js │ ├── eventDao.js │ ├── issueDao.js │ ├── repositoryDao.js │ └── userDao.js ├── img │ ├── default_img.png │ ├── logo.png │ └── welcome.png ├── net │ ├── address.js │ ├── index.js │ ├── netwrokCode.js │ └── qiniu │ │ ├── auth.js │ │ ├── conf.js │ │ ├── imageOps.js │ │ ├── index.js │ │ ├── rpc.js │ │ └── rs.js ├── router.js ├── store │ ├── actions │ │ ├── event.js │ │ ├── issue.js │ │ ├── login.js │ │ ├── repository.js │ │ └── user.js │ ├── index.js │ ├── reducers │ │ ├── event.js │ │ ├── index.js │ │ ├── issue.js │ │ ├── login.js │ │ ├── repository.js │ │ └── user.js │ └── type.js ├── style │ ├── constant.js │ ├── i18n.js │ ├── index.js │ └── lottie │ │ ├── animation-login.json │ │ └── animation-w800-h800.json └── utils │ ├── actionUtils.js │ ├── backUtils.js │ ├── eventUtils.js │ ├── filterUtils.js │ ├── htmlUtils.js │ ├── issueUtils.js │ ├── pulse │ └── PulseUtils.js │ ├── timeUtil.js │ └── trending │ ├── GitHubTrending.js │ ├── StringUtil.js │ ├── TrendingRepoModel.js │ └── TrendingUtil.js ├── babel.config.js ├── download.png ├── index.js ├── ios ├── GSYGithubApp-Bridging-Header.h ├── GSYGithubApp-tvOS │ └── Info.plist ├── GSYGithubApp-tvOSTests │ └── Info.plist ├── GSYGithubApp.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── GSYGithubApp-tvOS.xcscheme │ │ └── GSYGithubApp.xcscheme ├── GSYGithubApp.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── GSYGithubApp │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── logo-2.png │ │ │ ├── logo-3.png │ │ │ ├── logo-4.png │ │ │ ├── logo2x-2.png │ │ │ ├── logo2x-3.png │ │ │ └── logo2x.png │ │ ├── Contents.json │ │ ├── LaunchImage-1.launchimage │ │ │ ├── Contents.json │ │ │ ├── Default@2x.png │ │ │ ├── Default@3x-1.png │ │ │ ├── Default@3x-2.png │ │ │ ├── Default@3x-3.png │ │ │ ├── Default@3x-4.png │ │ │ └── Default@3x.png │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── Default@2x-1.png │ │ │ ├── Default@2x.png │ │ │ ├── Default@3x-1.png │ │ │ ├── Default@3x-2.png │ │ │ ├── Default@3x-3.png │ │ │ ├── Default@3x.png │ │ │ ├── Default_640x1136-1.png │ │ │ └── Default_640x1136.png │ ├── Info.plist │ └── main.m ├── GSYGithubAppTests │ ├── GSYGithubAppTests.m │ └── Info.plist ├── Podfile ├── Podfile.lock └── tmp.swift ├── ios_wait.png ├── logo.png ├── metro.config.js ├── package.json ├── register0.jpg ├── register1.jpg ├── thanks.jpg └── yarn.lock /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/Libraries/react-native/react-native-interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation' 40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 41 | 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\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 51 | 52 | [lints] 53 | sketchy-null-number=warn 54 | sketchy-null-mixed=warn 55 | sketchy-number=warn 56 | untyped-type-import=warn 57 | nonstrict-import=warn 58 | deprecated-type=warn 59 | unsafe-getters-setters=warn 60 | inexact-spread=warn 61 | unnecessary-invariant=warn 62 | signature-verification-failure=warn 63 | deprecated-utility=error 64 | 65 | [strict] 66 | deprecated-type 67 | nonstrict-import 68 | sketchy-null 69 | unclear-type 70 | unsafe-getters-setters 71 | untyped-import 72 | untyped-type-import 73 | 74 | [version] 75 | ^0.105.0 76 | -------------------------------------------------------------------------------- /.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 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | app/config/ignoreConfig.js 61 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/1.gif -------------------------------------------------------------------------------- /1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/1.jpg -------------------------------------------------------------------------------- /2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/2.jpg -------------------------------------------------------------------------------- /3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/3.jpg -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, {Component} from 'react'; 8 | import {View} from 'react-native'; 9 | import {Provider} from 'react-redux'; 10 | import getRouter from './app/router'; 11 | import store from './app/store/' 12 | import {getLanguageCurrent} from './app/utils/actionUtils' 13 | import {changeLocale} from './app/style/i18n' 14 | import {getRefreshHandler} from './app/utils/actionUtils' 15 | import * as Constant from './app/style/constant' 16 | 17 | export default class App extends Component<{}> { 18 | 19 | constructor() { 20 | super(); 21 | this.state = { 22 | store: store, 23 | show: false 24 | }; 25 | getLanguageCurrent().then((res) => { 26 | changeLocale(res.language); 27 | this.setState({ 28 | show: true 29 | }) 30 | }); 31 | 32 | //切换语言 33 | getRefreshHandler().set(Constant.REFRESH_LANGUAGE, () => { 34 | this.setState({ 35 | show: false 36 | }); 37 | setTimeout(() => { 38 | this.setState({ 39 | show: true 40 | }) 41 | }, 500) 42 | }) 43 | } 44 | 45 | 46 | render() { 47 | if (!this.state.show) { 48 | return 49 | } 50 | return ( 51 | 52 | {getRouter()} 53 | 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /CLEAR_CACHE.md: -------------------------------------------------------------------------------- 1 | cd GSYGithubApp project 2 | 3 | rm -rf ~/.rncache 4 | rm -rf $TMPDIR/* 5 | rm -rf node_modules/ 6 | npm install node_modules 7 | -------------------------------------------------------------------------------- /FURTURE.md: -------------------------------------------------------------------------------- 1 | 2 | * 升级RN版本 3 | 4 | - lottie ios swift error 5 | 6 | https://stackoverflow.com/questions/52536380/why-linker-link-static-libraries-with-errors-ios 7 | 8 | - This copy of libswiftCore.dylib requires an OS version prior to 12.2.0 9 | 10 | 添加一个空的 swift 文件 11 | 12 | - react-native-vector-icons 和 realm 目前还是使用了 link 的模式 13 | -------------------------------------------------------------------------------- /__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.gsygithubapp", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.gsygithubapp", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/debug.keystore -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/AntDesign.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/AntDesign.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Fontisto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Fontisto.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /android/app/src/main/java/com/gsygithubapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.gsygithubapp; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "GSYGithubApp"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/gsygithubapp/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.gsygithubapp; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import android.content.Context; 7 | import com.facebook.react.PackageList; 8 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory; 9 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 10 | import com.facebook.react.ReactApplication; 11 | import com.oblador.vectoricons.VectorIconsPackage; 12 | import com.facebook.react.ReactNativeHost; 13 | import com.facebook.react.ReactPackage; 14 | import com.facebook.soloader.SoLoader; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.util.List; 17 | 18 | public class MainApplication extends Application implements ReactApplication { 19 | 20 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 21 | @Override 22 | public boolean getUseDeveloperSupport() { 23 | return BuildConfig.DEBUG; 24 | } 25 | 26 | @Override 27 | protected List getPackages() { 28 | @SuppressWarnings("UnnecessaryLocalVariable") 29 | List packages = new PackageList(this).getPackages(); 30 | // Packages that cannot be autolinked yet can be added manually here, for example: 31 | // packages.add(new MyReactNativePackage()); 32 | return packages; 33 | } 34 | 35 | @Override 36 | protected String getJSMainModuleName() { 37 | return "index"; 38 | } 39 | }; 40 | 41 | @Override 42 | public ReactNativeHost getReactNativeHost() { 43 | return mReactNativeHost; 44 | } 45 | 46 | @Override 47 | public void onCreate() { 48 | super.onCreate(); 49 | SoLoader.init(this, /* native exopackage */ false); 50 | initializeFlipper(this); // Remove this line if you don't want Flipper enabled 51 | } 52 | 53 | /** 54 | * Loads Flipper in React Native templates. 55 | * 56 | * @param context 57 | */ 58 | private static void initializeFlipper(Context context) { 59 | if (BuildConfig.DEBUG) { 60 | try { 61 | /* 62 | We use reflection here to pick up the class that initializes Flipper, 63 | since Flipper library is not available in release mode 64 | */ 65 | Class aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); 66 | aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); 67 | } catch (ClassNotFoundException e) { 68 | e.printStackTrace(); 69 | } catch (NoSuchMethodException e) { 70 | e.printStackTrace(); 71 | } catch (IllegalAccessException e) { 72 | e.printStackTrace(); 73 | } catch (InvocationTargetException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | GSYGithubApp 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "28.0.3" 6 | minSdkVersion = 16 7 | compileSdkVersion = 28 8 | targetSdkVersion = 28 9 | supportLibVersion = "28.0.0" 10 | } 11 | repositories { 12 | google() 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath("com.android.tools.build:gradle:3.4.2") 17 | 18 | 19 | // NOTE: Do not place your application dependencies here; they belong 20 | // in the individual module build.gradle files 21 | } 22 | } 23 | 24 | allprojects { 25 | repositories { 26 | mavenLocal() 27 | maven { 28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 29 | url("$rootDir/../node_modules/react-native/android") 30 | } 31 | maven { 32 | // Android JSC is installed from npm 33 | url("$rootDir/../node_modules/jsc-android/dist") 34 | } 35 | maven { url "https://jitpack.io" } 36 | google() 37 | jcenter() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem http://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /android/gsygithubapp-debug.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/android/gsygithubapp-debug.jks -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'GSYGithubApp' 2 | include ':react-native-vector-icons' 3 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 4 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 5 | include ':app' 6 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GSYGithubApp", 3 | "displayName": "GSYGithubApp" 4 | } -------------------------------------------------------------------------------- /app/components/MyPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import {InteractionManager} from 'react-native'; 7 | import loginActions from '../store/actions/login' 8 | import userActions from '../store/actions/user' 9 | import {connect} from 'react-redux' 10 | import {bindActionCreators} from 'redux' 11 | import BasePersonPage from "./widget/BasePersonPage"; 12 | 13 | /** 14 | * 我的 15 | */ 16 | 17 | @connect( 18 | state => ({ 19 | userState: state.user, 20 | }),dispatch => ({ 21 | loginAction: bindActionCreators(loginActions, dispatch), 22 | userAction: bindActionCreators(userActions, dispatch) 23 | }) 24 | ) 25 | export default class MyPage extends BasePersonPage { 26 | 27 | constructor(props) { 28 | super(props); 29 | this.refreshUnRead = this.refreshUnRead.bind(this); 30 | this.showType = 0; 31 | } 32 | 33 | componentDidMount() { 34 | super.componentDidMount(); 35 | InteractionManager.runAfterInteractions(() => { 36 | this.refreshUnRead(); 37 | this._getOrgsList(); 38 | }); 39 | } 40 | 41 | _refresh() { 42 | super._refresh(); 43 | this.refreshUnRead(); 44 | userActions.getUserInfo().then((res)=>{ 45 | if(__DEV__) { 46 | console.log("***MyPage***", res) 47 | } 48 | }) 49 | } 50 | 51 | getBackNotifyCall() { 52 | this.refreshUnRead(); 53 | } 54 | 55 | refreshUnRead() { 56 | userActions.getNotifation(false, false, 0).then((res) => { 57 | if (res && res.result && res.data && res.data.length > 0) { 58 | this.setState({ 59 | unRead: true, 60 | }); 61 | } else { 62 | this.setState({ 63 | unRead: false, 64 | }) 65 | } 66 | }) 67 | } 68 | 69 | getUserInfo() { 70 | let {userState} = this.props; 71 | return (userState.userInfo) ? userState.userInfo : {}; 72 | } 73 | 74 | getSetting() { 75 | return true 76 | } 77 | 78 | getSettingNeed() { 79 | return true 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/components/NotifyPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component, PureComponent} from 'react'; 6 | import { 7 | View, InteractionManager, StatusBar, Dimensions, StyleSheet, DeviceEventEmitter, 8 | } from 'react-native'; 9 | import {Actions, Tabs} from 'react-native-router-flux'; 10 | import styles from "../style" 11 | import * as Constant from "../style/constant" 12 | import I18n from '../style/i18n' 13 | import userActions from '../store/actions/user' 14 | import ListPage from "./ListPage"; 15 | import ScrollableTabView, {DefaultTabBar} from 'react-native-scrollable-tab-view-fix-guo'; 16 | import {launchUrl} from "../utils/htmlUtils"; 17 | 18 | /** 19 | * 通知页面 20 | */ 21 | class NotifyPage extends Component { 22 | 23 | constructor(props) { 24 | super(props); 25 | this.page = 2; 26 | this._refresh = this._refresh.bind(this); 27 | this._asRead = this._asRead.bind(this); 28 | this.index = 0; 29 | } 30 | 31 | componentDidMount() { 32 | this.subscription = DeviceEventEmitter.addListener( 33 | `NotifyPage`, 34 | (params) => { 35 | if (params && params.type === "allRead") { 36 | params.type = ""; 37 | this._refresh(); 38 | } 39 | }); 40 | } 41 | 42 | componentWillUnmount() { 43 | this.subscription.remove(); 44 | this.props.backNotifyCall && this.props.backNotifyCall() 45 | } 46 | 47 | _refresh() { 48 | if (this.unReadList) 49 | this.unReadList._refresh(); 50 | if (this.partList) 51 | this.partList._refresh(); 52 | if (this.allList) 53 | this.allList._refresh(); 54 | } 55 | 56 | _handleIndexChange = index => this.setState({index}); 57 | 58 | 59 | _asRead(id) { 60 | userActions.setNotificationAsRead(id).then(() => { 61 | this._refresh(); 62 | }) 63 | } 64 | 65 | 66 | render() { 67 | return ( 68 | 69 | 123 | ) 124 | } 125 | } 126 | 127 | NotifyPage.defaultProps = {}; 128 | 129 | 130 | export default NotifyPage 131 | -------------------------------------------------------------------------------- /app/components/PersonPage.js: -------------------------------------------------------------------------------- 1 | import BasePersonPage from "./widget/BasePersonPage"; 2 | import userAction from "../store/actions/user"; 3 | import {Actions} from "react-native-router-flux"; 4 | import store from "../store"; 5 | 6 | const {dispatch, getState} = store; 7 | 8 | /** 9 | * 用户信息页面 10 | */ 11 | class PersonPage extends BasePersonPage { 12 | constructor(props) { 13 | super(props); 14 | this._onClose = this._onClose.bind(this); 15 | this._refreshInfo = this._refreshInfo.bind(this); 16 | this.state = { 17 | userInfo: { 18 | login: this.props.currentUser, 19 | followers: '---', 20 | star: '---', 21 | following: '---', 22 | public_repos: '---', 23 | }, 24 | needFollow: false, 25 | hadFollowed: false, 26 | } 27 | } 28 | 29 | componentDidMount() { 30 | this._refreshInfo(); 31 | super.componentDidMount(); 32 | } 33 | 34 | componentWillUnmount() { 35 | } 36 | 37 | _onClose() { 38 | Actions.pop(); 39 | return true; 40 | } 41 | 42 | getUserInfo() { 43 | return this.state.userInfo; 44 | } 45 | 46 | getNeedFollow() { 47 | let login = getState()['user'].userInfo.login; 48 | return this.state.needFollow && (this.props.currentUser !== login); 49 | } 50 | 51 | getHanFollow() { 52 | return this.state.hadFollowed; 53 | } 54 | 55 | _refreshInfo() { 56 | userAction.getPersonUserInfo(this.props.currentUser).then((res) => { 57 | if (res && res.result) { 58 | this.setState({ 59 | userInfo: res.data 60 | }); 61 | if (res.data.type === "Organization") { 62 | Actions.refresh({titleData: res.data, showType: "Organization"}); 63 | } else { 64 | Actions.refresh({titleData: res.data, showType: "user"}); 65 | } 66 | } 67 | return res.next(); 68 | }).then((res) => { 69 | if (res && res.result) { 70 | this.setState({ 71 | userInfo: res.data 72 | }); 73 | if (res.data.type === "Organization") { 74 | Actions.refresh({titleData: res.data, showType: "Organization"}); 75 | } else { 76 | Actions.refresh({titleData: res.data, showType: "user"}); 77 | } 78 | } 79 | }); 80 | userAction.checkFollow(this.props.currentUser).then((res) => { 81 | this.setState({ 82 | hadFollowed: res.result, 83 | needFollow: true 84 | }) 85 | }); 86 | } 87 | 88 | doFollowLogic() { 89 | Actions.LoadingModal({backExit: false}); 90 | userAction.doFollow(this.props.currentUser, !this.state.hadFollowed).then(() => { 91 | this._refreshInfo(); 92 | setTimeout(() => { 93 | Actions.pop(); 94 | }, 500); 95 | }) 96 | } 97 | } 98 | 99 | export default PersonPage -------------------------------------------------------------------------------- /app/components/PhotoPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, ActivityIndicator, StatusBar 8 | } from 'react-native'; 9 | import {Actions} from 'react-native-router-flux'; 10 | import {screenWidth, screenHeight} from "../style" 11 | import ImageViewer from 'react-native-image-zoom-viewer' 12 | import styles from "../style/index"; 13 | import * as Constant from "../style/constant"; 14 | 15 | /** 16 | * 大图查看 17 | */ 18 | class PhotoPage extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.images = []; 23 | this.exit = false; 24 | this.images.push({url: this.props.uri}); 25 | } 26 | 27 | componentDidMount() { 28 | } 29 | 30 | componentWillUnmount() { 31 | 32 | } 33 | 34 | 35 | render() { 36 | let loading = 37 | 43 | 48 | ; 49 | return ( 50 | 51 | 70 | ) 71 | } 72 | } 73 | 74 | 75 | export default PhotoPage -------------------------------------------------------------------------------- /app/components/RecommendPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Text, StatusBar 8 | } from 'react-native'; 9 | import styles from "../style" 10 | import loginActions from '../store/actions/login' 11 | import {connect} from 'react-redux' 12 | import {bindActionCreators} from 'redux' 13 | import EventItem from './widget/EventItem' 14 | import CommonRowItem from './common/CommonRowItem' 15 | import RepositoryItem from './widget/RepositoryItem' 16 | import IssueItem from './widget/IssueHead' 17 | 18 | /** 19 | * 推荐 20 | */ 21 | class RecommendPage extends Component { 22 | componentDidMount() { 23 | } 24 | 25 | componentWillUnmount() { 26 | 27 | } 28 | 29 | 30 | render() { 31 | let {loginActions} = this.props; 32 | return ( 33 | 34 | 67 | ) 68 | } 69 | } 70 | 71 | 72 | export default connect(state => ( {state}), dispatch => ({ 73 | loginActions: bindActionCreators(loginActions, dispatch) 74 | }) 75 | )(RecommendPage) 76 | -------------------------------------------------------------------------------- /app/components/ReleasePage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component,} from 'react'; 6 | import { 7 | View, InteractionManager, StatusBar, Dimensions, 8 | } from 'react-native'; 9 | import styles from "../style" 10 | import * as Constant from "../style/constant" 11 | import I18n from '../style/i18n' 12 | import {TabView, TabBar,} from 'react-native-tab-view'; 13 | import ListPage from "./ListPage"; 14 | 15 | /** 16 | * 仓库发布列表 17 | */ 18 | class ReleasePage extends Component { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.page = 2; 23 | this._refresh = this._refresh.bind(this); 24 | this.state = { 25 | index: 0, 26 | routes: [ 27 | {key: '1', title: I18n('reposRelease')}, 28 | {key: '2', title: I18n('reposTag')}, 29 | ], 30 | } 31 | } 32 | 33 | componentDidMount() { 34 | InteractionManager.runAfterInteractions(() => { 35 | this._refresh(); 36 | }); 37 | } 38 | 39 | componentWillUnmount() { 40 | 41 | } 42 | 43 | _refresh() { 44 | 45 | } 46 | 47 | _handleIndexChange = index => this.setState({index}); 48 | 49 | _renderHeader = props => 50 | ; 55 | 56 | _renderScene = ({route}) => { 57 | switch (route.key) { 58 | case '1': 59 | return ( 60 | 66 | ); 67 | case '2': 68 | return ( 69 | 75 | ); 76 | default: 77 | return null; 78 | } 79 | }; 80 | 81 | 82 | render() { 83 | return ( 84 | 85 | 102 | ) 103 | } 104 | } 105 | 106 | ReleasePage.defaultProps = {}; 107 | 108 | 109 | export default ReleasePage 110 | -------------------------------------------------------------------------------- /app/components/WelcomePage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Image, StatusBar, Platform, Animated, Easing 8 | } from 'react-native'; 9 | import {Actions} from 'react-native-router-flux'; 10 | import styles, {screenHeight, screenWidth} from "../style" 11 | import loginActions from '../store/actions/login' 12 | import userActions from '../store/actions/user' 13 | import {connect} from 'react-redux' 14 | import {bindActionCreators} from 'redux' 15 | import * as Constant from "../style/constant" 16 | import LottieView from 'lottie-react-native'; 17 | 18 | /** 19 | * 欢迎页 20 | */ 21 | 22 | @connect( 23 | state => ({ 24 | state 25 | }), dispatch => ({ 26 | actions: bindActionCreators(loginActions, dispatch), 27 | }) 28 | ) 29 | export default class WelcomePage extends Component { 30 | 31 | constructor(props) { 32 | super(props); 33 | this.toNext = this.toNext.bind(this); 34 | this.state = { 35 | progress: new Animated.Value(0), 36 | }; 37 | } 38 | 39 | 40 | componentDidMount() { 41 | //是否登陆,是否用户信息 42 | userActions.initUserInfo().then((res) => { 43 | this.toNext(res) 44 | }); 45 | Animated.timing(this.state.progress, { 46 | toValue: 1, 47 | duration: 2000, 48 | easing: Easing.linear, 49 | }).start(); 50 | } 51 | 52 | componentWillUnmount() { 53 | if (this.refs.lottieView) { 54 | this.refs.lottieView.reset(); 55 | } 56 | } 57 | 58 | toNext(res) { 59 | setTimeout(() => { 60 | if (res && res.result) { 61 | Actions.reset("root"); 62 | } else { 63 | Actions.reset("LoginPage"); 64 | } 65 | }, 2100); 66 | } 67 | 68 | render() { 69 | return ( 70 | 71 | 91 | ) 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /app/components/common/CommonBottomBar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableOpacity 4 | } from 'react-native'; 5 | import PropTypes from 'prop-types'; 6 | import styles from "../../style/index" 7 | import * as Constant from "../../style/constant" 8 | import IconC from 'react-native-vector-icons/Octicons' 9 | 10 | /** 11 | * 通用横向按键控件 12 | */ 13 | class CommonBottomBar extends Component { 14 | 15 | constructor(props) { 16 | super(props); 17 | this.renderItem = this.renderItem.bind(this); 18 | } 19 | 20 | componentDidMount() { 21 | } 22 | 23 | componentWillUnmount() { 24 | 25 | } 26 | 27 | renderItem(data) { 28 | let iconColor = data.iconColor ? data.iconColor : Constant.primaryColor; 29 | let textColor = data.itemTextColor ? data.itemTextColor : Constant.primaryColor; 30 | return ( 31 | { 33 | data.itemClick && data.itemClick(data); 34 | }} 35 | key={data.itemName}> 36 | 37 | 41 | {data.itemName} 42 | 43 | 44 | ) 45 | } 46 | 47 | render() { 48 | let {dataList, rootStyles} = this.props; 49 | let items = []; 50 | dataList.forEach((data) => { 51 | items.push(this.renderItem(data)) 52 | }); 53 | return ( 54 | 57 | {items} 58 | 59 | ) 60 | } 61 | } 62 | 63 | 64 | CommonBottomBar.propTypes = { 65 | dataList: PropTypes.array, 66 | rootStyles: PropTypes.any, 67 | }; 68 | 69 | 70 | CommonBottomBar.defaultProps = { 71 | dataList: [], 72 | rootStyles: {} 73 | }; 74 | 75 | export default CommonBottomBar -------------------------------------------------------------------------------- /app/components/common/CommonConfirmModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/12. 3 | */ 4 | import React, {Component} from 'react'; 5 | import { 6 | Text, 7 | View, 8 | TouchableOpacity, 9 | } from 'react-native'; 10 | import PropTypes from 'prop-types'; 11 | import styles, {screenWidth, screenHeight} from "../../style/index" 12 | import * as Constant from "../../style/constant" 13 | import I18n from '../../style/i18n' 14 | import Modal from './ModalBox'; 15 | import {Actions} from "react-native-router-flux"; 16 | 17 | 18 | /** 19 | * 确认弹出模态框 20 | */ 21 | class CommonConfirmModal extends Component { 22 | 23 | constructor(props) { 24 | super(props); 25 | this.onClose = this.onClose.bind(this); 26 | this.text = this.props.text; 27 | } 28 | 29 | componentDidMount() { 30 | if (this.refs.loginModal) 31 | this.refs.loginModal.open(); 32 | } 33 | 34 | componentWillUnmount() { 35 | } 36 | 37 | onClose() { 38 | Actions.pop(); 39 | return true; 40 | } 41 | 42 | render() { 43 | let width = screenWidth - 100; 44 | return ( 45 | 53 | 54 | 55 | 57 | {this.props.titleText} 59 | 60 | 63 | {this.props.text} 67 | 68 | 73 | { 76 | Actions.pop(); 77 | }}> 78 | {I18n("cancel")} 79 | 80 | { 87 | if (this.text && this.text.trim().length > 0) { 88 | Actions.pop(); 89 | this.props.textConfirm && this.props.textConfirm(this.text); 90 | } 91 | }}> 92 | {I18n("ok")} 93 | 94 | 95 | 96 | 97 | 98 | ) 99 | } 100 | } 101 | 102 | CommonConfirmModal.propTypes = { 103 | text: PropTypes.string, 104 | titleText: PropTypes.string, 105 | textConfirm: PropTypes.func, 106 | }; 107 | CommonConfirmModal.defaultProps = { 108 | text: '', 109 | titleText: '', 110 | }; 111 | 112 | 113 | export default CommonConfirmModal; 114 | -------------------------------------------------------------------------------- /app/components/common/CommonIconButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Text, TouchableOpacity 8 | } from 'react-native'; 9 | import PropTypes from 'prop-types'; 10 | import {Router, Actions, Scene} from 'react-native-router-flux'; 11 | import styles from "../../style/index" 12 | import I18n from '../../style/i18n' 13 | import * as Constant from '../../style/constant' 14 | import Icon from 'react-native-vector-icons/FontAwesome' 15 | import Icon2 from 'react-native-vector-icons/Ionicons' 16 | import Icon3 from 'react-native-vector-icons/MaterialCommunityIcons' 17 | 18 | 19 | /** 20 | * 通用icon按键 21 | */ 22 | class CommonIconButton extends Component { 23 | 24 | static propTypes = { 25 | rightBtn: PropTypes.string, 26 | needRightBtn: PropTypes.bool, 27 | rightBtnPress: PropTypes.func 28 | }; 29 | 30 | constructor(props) { 31 | super(props); 32 | } 33 | 34 | 35 | componentDidMount() { 36 | } 37 | 38 | componentWillUnmount() { 39 | 40 | } 41 | 42 | render() { 43 | this.local = this.props; 44 | if (this.props.data) { 45 | this.local = this.props.data; 46 | } 47 | if (!this.local || !this.local.needRightBtn) { 48 | return 49 | } 50 | let icon = ; 51 | switch (this.local.iconType) { 52 | case 3: 53 | icon = 54 | break; 55 | case 2: 56 | icon = 57 | break; 58 | case 1: 59 | default: 60 | icon = 61 | break; 62 | } 63 | return ( 64 | { 70 | this.local.rightBtnPress && this.local.rightBtnPress(this.local); 71 | }}> 72 | {icon} 73 | 74 | ) 75 | } 76 | } 77 | 78 | CommonIconButton.propTypes = { 79 | rightBtn: PropTypes.string, 80 | iconType: PropTypes.number, 81 | needRightBtn: PropTypes.bool, 82 | rightBtnPress: PropTypes.func 83 | }; 84 | 85 | CommonIconButton.defaultProps = { 86 | iconType: 1, 87 | }; 88 | 89 | export default CommonIconButton 90 | -------------------------------------------------------------------------------- /app/components/common/CommonInputBar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableOpacity, ScrollView 4 | } from 'react-native'; 5 | import PropTypes from 'prop-types'; 6 | import styles from "../../style/index" 7 | import * as Constant from "../../style/constant" 8 | import IconC from 'react-native-vector-icons/Octicons' 9 | import IconC2 from 'react-native-vector-icons/MaterialCommunityIcons' 10 | 11 | /** 12 | * 通用输入框的快捷按键 13 | */ 14 | class CommonInputBar extends Component { 15 | 16 | constructor(props) { 17 | super(props); 18 | this.renderItem = this.renderItem.bind(this); 19 | } 20 | 21 | componentDidMount() { 22 | } 23 | 24 | componentWillUnmount() { 25 | 26 | } 27 | 28 | renderItem(data) { 29 | let iconColor = data.iconColor ? data.iconColor : Constant.primaryColor; 30 | 31 | let icon = ; 32 | switch (data.iconType) { 33 | case 1: 34 | icon = ; 38 | break; 39 | case 2: 40 | icon = ; 44 | break; 45 | } 46 | return ( 47 | { 49 | data.itemClick && data.itemClick(data); 50 | }} 51 | key={data.icon}> 52 | {icon} 53 | 54 | ) 55 | } 56 | 57 | render() { 58 | let {dataList, rootStyles} = this.props; 59 | let items = []; 60 | dataList.forEach((data) => { 61 | items.push(this.renderItem(data)) 62 | }); 63 | return ( 64 | 65 | 67 | {items} 68 | 69 | 70 | ) 71 | } 72 | } 73 | 74 | 75 | CommonInputBar.propTypes = { 76 | dataList: PropTypes.array, 77 | rootStyles: PropTypes.any, 78 | inputView: PropTypes.any, 79 | }; 80 | 81 | 82 | CommonInputBar.defaultProps = { 83 | dataList: [], 84 | rootStyles: {} 85 | }; 86 | 87 | export default CommonInputBar -------------------------------------------------------------------------------- /app/components/common/CommonNameValueItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableOpacity 4 | } from 'react-native'; 5 | import styles from "../../style/index" 6 | import PropTypes from 'prop-types'; 7 | import * as Constant from '../../style/constant' 8 | 9 | /** 10 | * 垂直两行文本按键item 11 | */ 12 | class CommonNameValueItem extends Component { 13 | 14 | constructor(props) { 15 | super(props) 16 | } 17 | 18 | render() { 19 | let halfEdge = Constant.normalMarginEdge / 2; 20 | let bottomTopTextStyle = [styles.subSmallText, { 21 | marginTop: halfEdge 22 | },]; 23 | let bottomBottomTextStyle = [styles.subLightSmallText, {marginVertical: halfEdge}]; 24 | return ( 25 | { 26 | this.props.onItemPress && this.props.onItemPress(); 27 | }}> 28 | {this.props.itemName} 29 | {this.props.itemValue} 30 | 31 | ) 32 | } 33 | } 34 | 35 | CommonNameValueItem.propTypes = { 36 | itemStyle: PropTypes.any, 37 | itemName: PropTypes.string, 38 | itemValue: PropTypes.string, 39 | onItemPress: PropTypes.func, 40 | }; 41 | 42 | export default CommonNameValueItem; -------------------------------------------------------------------------------- /app/components/common/CommonOptionModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/12. 3 | */ 4 | import React, {Component} from 'react'; 5 | import { 6 | Text, 7 | View, 8 | TouchableOpacity, 9 | ScrollView 10 | } from 'react-native'; 11 | import PropTypes from 'prop-types'; 12 | import styles, {screenWidth, screenHeight} from "../../style/index" 13 | import * as Constant from "../../style/constant" 14 | import Modal from './ModalBox'; 15 | import {Actions} from "react-native-router-flux"; 16 | 17 | const width = screenWidth - 100; 18 | const itemHeight = 50; 19 | 20 | /** 21 | * 通用配置item选择modal 22 | */ 23 | class CommonOptionModal extends Component { 24 | 25 | constructor(props) { 26 | super(props); 27 | this.onClose = this.onClose.bind(this); 28 | this._renderItem = this._renderItem.bind(this); 29 | } 30 | 31 | componentDidMount() { 32 | if (this.refs.modal) 33 | this.refs.modal.open(); 34 | } 35 | 36 | componentWillUnmount() { 37 | } 38 | 39 | onClose() { 40 | Actions.pop(); 41 | return true; 42 | } 43 | 44 | _renderItem(data) { 45 | return ( 46 | { 49 | Actions.pop(); 50 | data.itemClick && data.itemClick(data); 51 | }} 52 | key={data.itemName}> 53 | {data.itemName} 54 | 55 | ) 56 | } 57 | 58 | render() { 59 | let {dataList} = this.props; 60 | let items = []; 61 | dataList.forEach((data) => { 62 | items.push(this._renderItem(data)) 63 | }); 64 | let sumHeight = itemHeight * items.length + 2; 65 | let currentHeight = (sumHeight >= screenHeight) ? screenHeight : sumHeight; 66 | return ( 67 | 75 | 76 | 77 | 82 | {items} 83 | 84 | 85 | 86 | 87 | ) 88 | } 89 | } 90 | 91 | CommonOptionModal.propTypes = { 92 | dataList: PropTypes.array, 93 | }; 94 | 95 | 96 | CommonOptionModal.defaultProps = { 97 | dataList: [], 98 | }; 99 | 100 | export default CommonOptionModal; 101 | -------------------------------------------------------------------------------- /app/components/common/CommonRowItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | import React, { 5 | Component, 6 | } from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import { 9 | Text, 10 | Image, 11 | View, 12 | TouchableOpacity, 13 | Dimensions, 14 | StyleSheet 15 | } from 'react-native'; 16 | 17 | import * as Constant from '../../style/constant' 18 | import styles from '../../style/index' 19 | import Icon from 'react-native-vector-icons/EvilIcons' 20 | import IconC from 'react-native-vector-icons/Octicons' 21 | 22 | 23 | /** 24 | * 通用行Item 25 | */ 26 | class CommonRowItem extends Component { 27 | constructor(props) { 28 | super(props) 29 | } 30 | 31 | componentDidMount() { 32 | } 33 | 34 | componentWillUnmount() { 35 | 36 | } 37 | 38 | 39 | render() { 40 | let {onClickFun, itemText, showIconNext, textStyle, nameStyle, topLine, bottomLine, itemIcon, iconSize, nameText} = this.props; 41 | let IconComponent = (showIconNext) ? 42 | 43 | : ; 44 | let tl = (topLine) ? StyleSheet.hairlineWidth : 0; 45 | let bl = (bottomLine) ? StyleSheet.hairlineWidth : 0; 46 | let leftIcon = (itemIcon) ? 47 | 48 | : ; 49 | let leftText = (nameText) ? 50 | {nameText} 51 | : ; 52 | return ( 53 | { 55 | onClickFun && onClickFun() 56 | }} 57 | style={[{ 58 | minHeight: 50, marginLeft: Constant.normalMarginEdge, marginRight: Constant.normalMarginEdge, 59 | marginVertical: Constant.normalMarginEdge / 2, 60 | }, ...this.props.viewStyle]}> 61 | 64 | {leftIcon} 65 | {leftText} 66 | {itemText} 67 | {IconComponent} 68 | 69 | 70 | ); 71 | } 72 | } 73 | 74 | const propTypes = { 75 | itemText: PropTypes.string, 76 | onClickFun: PropTypes.func, 77 | showIconNext: PropTypes.bool, 78 | topLine: PropTypes.bool, 79 | bottomLine: PropTypes.bool, 80 | itemIcon: PropTypes.string, 81 | iconSize: PropTypes.number, 82 | itemIconColor: PropTypes.string, 83 | nameText: PropTypes.string, 84 | itemIconSize: PropTypes.number, 85 | textStyle: PropTypes.any, 86 | viewStyle: PropTypes.array, 87 | }; 88 | CommonRowItem.propTypes = propTypes; 89 | CommonRowItem.defaultProps = { 90 | showIconNext: true, 91 | topLine: true, 92 | bottomLine: true, 93 | textStyle: styles.smallText, 94 | itemIconColor: Constant.primaryColor, 95 | itemIconSize: Constant.smallIconSize, 96 | viewStyle: [], 97 | }; 98 | 99 | export default CommonRowItem; -------------------------------------------------------------------------------- /app/components/common/LoadingModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/12. 3 | */ 4 | import React, {Component} from 'react'; 5 | import { 6 | Text, 7 | View, 8 | BackHandler 9 | } from 'react-native'; 10 | import PropTypes from 'prop-types'; 11 | import styles, {screenWidth, screenHeight} from "../../style/index" 12 | import I18n from '../../style/i18n' 13 | import Modal from './ModalBox'; 14 | import Spinner from 'react-native-spinkit-fix-new'; 15 | import {Actions} from "react-native-router-flux"; 16 | 17 | 18 | /** 19 | * 加载中Modal 20 | */ 21 | class LoadingModal extends Component { 22 | 23 | constructor(props) { 24 | super(props); 25 | this.onClose = this.onClose.bind(this); 26 | } 27 | 28 | componentDidMount() { 29 | if (this.refs.loginModal) { 30 | this.refs.loginModal.open(); 31 | } 32 | this.handle = BackHandler.addEventListener('loaddingBack', this.onClose) 33 | } 34 | 35 | componentWillUnmount() { 36 | if (this.handle) { 37 | this.handle.remove(); 38 | } 39 | } 40 | 41 | onClose() { 42 | Actions.pop(); 43 | return true; 44 | } 45 | 46 | render() { 47 | return ( 48 | 54 | 55 | 56 | 60 | {this.props.text} 61 | 62 | 63 | 64 | ) 65 | } 66 | } 67 | 68 | LoadingModal.propTypes = { 69 | text: PropTypes.string, 70 | backExit: PropTypes.bool, 71 | }; 72 | LoadingModal.defaultProps = { 73 | text: I18n('loading'), 74 | backExit: true, 75 | }; 76 | 77 | 78 | export default LoadingModal; 79 | -------------------------------------------------------------------------------- /app/components/common/LogHoc.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, useReducer, useRef, useImperativeHandle, forwardRef 3 | } from 'react'; 4 | 5 | import { 6 | Text, 7 | Image, 8 | View, 9 | TouchableOpacity, 10 | Dimensions, 11 | StyleSheet 12 | } from 'react-native'; 13 | 14 | export function logHoc(WrappedComponent) { 15 | 16 | return class extends Component { 17 | 18 | componentDidMount() { 19 | this.start = Date.now(); 20 | this.displayName = this.getDisplayName(this); 21 | this.end = Date.now(); 22 | if (__DEV__) { 23 | console.log(this.props) 24 | console.log(`logHoc ${this.displayName} 渲染时间:${this.end - this.start} ms`); 25 | } 26 | } 27 | 28 | componentWillUnmount() { 29 | if (__DEV__) { 30 | console.log(`logHoc 退出${this.displayName}`); 31 | } 32 | } 33 | 34 | getDisplayName(WrappedComponent) { 35 | return WrappedComponent.displayName || WrappedComponent.props.name || 'Component'; 36 | } 37 | 38 | render() { 39 | return 40 | } 41 | } 42 | } 43 | 44 | 45 | /** 46 | * 47 | * React Hooks 实现 reducer Demo 48 | * 49 | **/ 50 | 51 | const initialState = {count: 0}; 52 | 53 | function reducer(state, action) { 54 | switch (action.type) { 55 | case 'reset': 56 | return initialState; 57 | case 'increment': 58 | return {count: state.count + 1}; 59 | case 'decrement': 60 | return {count: state.count - 1}; 61 | default: 62 | // A reducer must always return a valid state. 63 | // Alternatively you can throw an error if an invalid action is dispatched. 64 | return state; 65 | } 66 | } 67 | 68 | export function DemoCounter({initialCount}) { 69 | const [state, dispatch] = useReducer(reducer, {count: initialCount}); 70 | return ( 71 | 72 | Count: {state.count} 73 | dispatch({type: 'reset'})}> 74 | Reset 75 | 76 | dispatch({type: 'increment'})}> 77 | + 78 | 79 | dispatch({type: 'decrement'})}> 80 | - 81 | 82 | 83 | ) 84 | } 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/components/common/ToastProxy.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Toast from 'react-native-root-toast' 3 | 4 | /** 5 | * 文本提示框 6 | */ 7 | export default function toast(text, duration = Toast.durations.SHORT, position = Toast.positions.CENTER) { 8 | return Toast.show(text, { 9 | duration: duration, 10 | position: position, 11 | shadow: true, 12 | animation: true, 13 | hideOnPress: true, 14 | delay: 0, 15 | }); 16 | } 17 | 18 | export function hideToast(toast) { 19 | Toast.hide(toast) 20 | } 21 | -------------------------------------------------------------------------------- /app/components/widget/BottomPopmenuItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableHighlight, StyleSheet 4 | } from 'react-native'; 5 | import styles, {screenWidth} from "../../style" 6 | import * as Constant from "../../style/constant" 7 | import ModalDropdown from '../common/ModalDropdown'; 8 | import PropTypes from 'prop-types'; 9 | import Icon from 'react-native-vector-icons/Octicons' 10 | 11 | /** 12 | * 底部弹出框Item 13 | */ 14 | class BottomPopmenuItem extends Component { 15 | 16 | constructor(props) { 17 | super(props); 18 | this.state = { 19 | icon: 'md-arrow-dropdown' 20 | } 21 | } 22 | 23 | componentDidMount() { 24 | 25 | } 26 | 27 | componentWillUnmount() { 28 | } 29 | 30 | render() { 31 | return ( 32 | 36 | 41 | { 44 | this.props.onSelect && this.props.onSelect(rowID, rowData); 45 | return true 46 | }} 47 | adjustFrame={this.props.adjustFrame} 48 | defaultIndex={this.props.defaultIndex} 49 | defaultValue={this.props.defaultValue} 50 | style={[{ 51 | backgroundColor: Constant.transparentColor, 52 | }, this.props.styles]} 53 | textStyle={{ 54 | fontSize: 13, 55 | width: 45, 56 | color: Constant.mainTextColor, 57 | textAlign: 'center', 58 | textAlignVertical: 'center', 59 | }} 60 | dropdownStyle={[{ 61 | width: screenWidth / 2, 62 | borderColor: Constant.primaryColor, 63 | borderWidth: 1, 64 | borderRadius: 2, 65 | }, this.props.dropdownStyle]} 66 | renderRow={this.renderRow.bind(this)} 67 | renderSeparator={(sectionID, rowID, adjacentRowHighlighted) => this.renderSeparator(sectionID, rowID, adjacentRowHighlighted)} 68 | /> 69 | 70 | ); 71 | } 72 | 73 | renderRow(rowData, rowID, highlighted) { 74 | return ( 75 | 76 | 83 | 88 | {rowData.name} 89 | 90 | 91 | 92 | ); 93 | } 94 | 95 | renderSeparator(sectionID, rowID, adjacentRowHighlighted) { 96 | let key = `spr_${rowID}`; 97 | return ( 98 | 100 | ); 101 | } 102 | } 103 | 104 | BottomPopmenuItem.propTypes = { 105 | itemHeight: PropTypes.number, 106 | defaultIndex: PropTypes.number, 107 | defaultValue: PropTypes.string, 108 | options: PropTypes.array, 109 | dropdownStyle: PropTypes.any, 110 | textStyle: PropTypes.any, 111 | adjustFrame: PropTypes.any, 112 | onSelect: PropTypes.func, 113 | }; 114 | 115 | 116 | export default BottomPopmenuItem 117 | -------------------------------------------------------------------------------- /app/components/widget/CodeFileItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | import React, { 5 | Component, 6 | } from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import { 9 | Text, 10 | Image, 11 | View, 12 | TouchableOpacity, 13 | Dimensions, 14 | StyleSheet 15 | } from 'react-native'; 16 | 17 | import * as Constant from '../../style/constant' 18 | import styles from '../../style' 19 | import Icon from 'react-native-vector-icons/FontAwesome' 20 | 21 | 22 | /** 23 | * 代码通用Item 24 | */ 25 | class CodeFileItem extends Component { 26 | constructor(props) { 27 | super(props) 28 | } 29 | 30 | componentDidMount() { 31 | } 32 | 33 | componentWillUnmount() { 34 | 35 | } 36 | 37 | render() { 38 | return ( 39 | 40 | ) 41 | } 42 | 43 | render() { 44 | let {onClickFun, itemText, titleStyle, itemIcon, itemTextTitle, needTitle} = this.props; 45 | let leftIcon = (itemIcon) ? 46 | 47 | : ; 48 | let title = (needTitle) ? 49 | 53 | {itemTextTitle} 54 | : ; 55 | return ( 56 | { 58 | onClickFun && onClickFun() 59 | }} 60 | style={[{ 61 | }, ...this.props.viewStyle]}> 62 | {title} 63 | 70 | {leftIcon} 71 | {itemText} 72 | 73 | 74 | ); 75 | } 76 | } 77 | 78 | const propTypes = { 79 | itemText: PropTypes.string, 80 | needTitle: PropTypes.bool, 81 | onClickFun: PropTypes.func, 82 | showIconNext: PropTypes.bool, 83 | topLine: PropTypes.bool, 84 | bottomLine: PropTypes.bool, 85 | itemIcon: PropTypes.string, 86 | itemIconColor: PropTypes.string, 87 | itemIconSize: PropTypes.number, 88 | textStyle: PropTypes.any, 89 | viewStyle: PropTypes.array, 90 | }; 91 | CodeFileItem.propTypes = propTypes; 92 | CodeFileItem.defaultProps = { 93 | showIconNext: true, 94 | topLine: true, 95 | bottomLine: true, 96 | needTitle: true, 97 | textStyle: styles.smallText, 98 | itemIconColor: Constant.primaryColor, 99 | itemIconSize: Constant.smallIconSize, 100 | viewStyle: [], 101 | }; 102 | 103 | export default CodeFileItem; -------------------------------------------------------------------------------- /app/components/widget/CustomBackButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Text, TouchableOpacity 8 | } from 'react-native'; 9 | import PropTypes from 'prop-types'; 10 | import {Router, Actions, Scene} from 'react-native-router-flux'; 11 | import styles from "../../style" 12 | import I18n from '../../style/i18n' 13 | import * as Constant from '../../style/constant' 14 | import Icon from 'react-native-vector-icons/Ionicons' 15 | 16 | /** 17 | * 自定义返回按键 18 | */ 19 | class BackButton extends Component { 20 | componentDidMount() { 21 | } 22 | 23 | componentWillUnmount() { 24 | 25 | } 26 | 27 | 28 | render() { 29 | if (this.props.hideBackButton) { 30 | return ; 31 | } 32 | return ( 33 | { 34 | Actions.pop(); 35 | }}> 36 | 37 | 38 | ) 39 | } 40 | } 41 | 42 | BackButton.propTypes = { 43 | hideBackButton: PropTypes.bool 44 | }; 45 | BackButton.defaultProps = { 46 | hideBackButton: false 47 | }; 48 | 49 | export default BackButton -------------------------------------------------------------------------------- /app/components/widget/CustomDrawerButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Text, TouchableOpacity 8 | } from 'react-native'; 9 | import PropTypes from 'prop-types'; 10 | import {Router, Actions, Scene} from 'react-native-router-flux'; 11 | import styles from "../../style" 12 | import I18n from '../../style/i18n' 13 | import * as Constant from '../../style/constant' 14 | import Icon from 'react-native-vector-icons/FontAwesome' 15 | 16 | /** 17 | * 自定义搜索过滤按键 18 | */ 19 | class CustomDrawerButton extends Component { 20 | componentDidMount() { 21 | } 22 | 23 | componentWillUnmount() { 24 | 25 | } 26 | 27 | 28 | render() { 29 | return ( 30 | 35 | 36 | 37 | ) 38 | } 39 | } 40 | 41 | export default CustomDrawerButton -------------------------------------------------------------------------------- /app/components/widget/CustomSearchButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/10. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, Text, TouchableOpacity 8 | } from 'react-native'; 9 | import PropTypes from 'prop-types'; 10 | import {Router, Actions, Scene} from 'react-native-router-flux'; 11 | import styles from "../../style" 12 | import I18n from '../../style/i18n' 13 | import * as Constant from '../../style/constant' 14 | import Icon from 'react-native-vector-icons/Ionicons' 15 | 16 | /** 17 | * 自定义搜索按键 18 | */ 19 | class SearchButton extends Component { 20 | componentDidMount() { 21 | } 22 | 23 | componentWillUnmount() { 24 | 25 | } 26 | 27 | 28 | render() { 29 | return ( 30 | { 34 | Actions.SearchPage(); 35 | }}> 36 | 37 | 38 | ) 39 | } 40 | } 41 | 42 | export default SearchButton -------------------------------------------------------------------------------- /app/components/widget/CustomWebComponent.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | Dimensions, 4 | Linking 5 | } from 'react-native'; 6 | import PropTypes from 'prop-types'; 7 | import { WebView } from 'react-native-webview'; 8 | import {Actions} from 'react-native-router-flux' 9 | import {launchUrl} from '../../utils/htmlUtils' 10 | 11 | let WebCurrent = WebView; 12 | 13 | const injectedScript = function () { 14 | function waitForBridge() { 15 | if (window.postMessage.length !== 1) { 16 | setTimeout(waitForBridge, 200); 17 | } 18 | else { 19 | let height = 0; 20 | if (document.documentElement.clientHeight > document.body.clientHeight) { 21 | height = document.documentElement.clientHeight 22 | } 23 | else { 24 | height = document.body.clientHeight 25 | } 26 | postMessage(height) 27 | } 28 | } 29 | 30 | waitForBridge(); 31 | }; 32 | 33 | /** 34 | * web控件 35 | */ 36 | export default class WebComponent extends Component { 37 | 38 | constructor(props: Object) { 39 | super(props); 40 | this.state = { 41 | loading: true, 42 | } 43 | } 44 | 45 | reload() { 46 | if (this.webview) { 47 | this.webview.reload(); 48 | } 49 | } 50 | 51 | static propTypes = { 52 | gsygithubLink: PropTypes.func, 53 | }; 54 | 55 | render() { 56 | const _w = this.props.width || Dimensions.get('window').width; 57 | const _h = this.props.width || Dimensions.get('window').height; 58 | return ( 59 | { 61 | this.webview = ref; 62 | }} 63 | onShouldStartLoadWithRequest={(event) => { 64 | if (event.url.indexOf('gsygithub://') === 0 ) { 65 | this.props.gsygithubLink && this.props.gsygithubLink(event.url); 66 | } else if (event.url && event.url.indexOf("https://github.com/") === 0) { 67 | launchUrl(event.url) 68 | } else if (event.url && (event.url.indexOf('http') === 0 || event.url.indexOf('www') === 0)) { 69 | Actions.WebPage({uri: event.url}); 70 | } else if (event.url !== 'about:blank') { 71 | Linking.openURL(event.url) 72 | } 73 | return event.url === 'about:blank'; 74 | }} 75 | injectedJavaScript={'(' + String(injectedScript) + ')();'} 76 | scrollEnabled={this.props.scrollEnabled || true} 77 | javaScriptEnabled={true} 78 | dataDetectorTypes={'none'} 79 | domStorageEnabled={true} 80 | automaticallyAdjustContentInsets={true} 81 | mixedContentMode={'always'} 82 | allowUniversalAccessFromFileURLs={true} 83 | mediaPlaybackRequiresUserAction={true} 84 | startInLoadingState={true} 85 | onLoadEnd={() => { 86 | this.setState({ 87 | loading: false, 88 | }) 89 | }} 90 | {...this.props} 91 | style={[{width: _w}, {flex: 1},]} 92 | /> 93 | ) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/components/widget/EventItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | import React, { 5 | Component, 6 | } from 'react' 7 | import { 8 | View, Text, TouchableOpacity 9 | } from 'react-native'; 10 | import PropTypes from 'prop-types'; 11 | import styles from '../../style' 12 | import * as Constant from '../../style/constant' 13 | import TimeText from './TimeText' 14 | import UserImage from './UserImage' 15 | 16 | /** 17 | * 事件列表Item 18 | */ 19 | class EventItem extends Component { 20 | constructor(props) { 21 | super(props) 22 | } 23 | 24 | componentDidMount() { 25 | } 26 | 27 | componentWillUnmount() { 28 | 29 | } 30 | 31 | render() { 32 | let {actionTime, actionUser, actionUserPic, actionMode, actionTarget} = this.props; 33 | let bottomDes = (this.props.des) ? 34 | 37 | {this.props.des} 38 | : ; 39 | let pic = (actionUserPic) ? : ; 48 | return ( 49 | { 59 | this.props.onPressItem && this.props.onPressItem(); 60 | }} 61 | > 62 | 63 | {pic} 64 | 65 | 68 | {actionUser} 69 | 70 | 73 | 74 | 75 | 76 | {actionTarget} 77 | 78 | {bottomDes} 79 | 80 | ) 81 | } 82 | } 83 | 84 | const propTypes = { 85 | actionTime: PropTypes.string, 86 | actionUser: PropTypes.string, 87 | actionUserPic: PropTypes.string, 88 | actionMode: PropTypes.string, 89 | actionTarget: PropTypes.string, 90 | des: PropTypes.string, 91 | onPressItem: PropTypes.func, 92 | }; 93 | 94 | EventItem.propTypes = propTypes; 95 | 96 | export default EventItem -------------------------------------------------------------------------------- /app/components/widget/IconTextAutoLinkItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Linking,Clipboard 4 | } from 'react-native'; 5 | import PropTypes from 'prop-types'; 6 | import styles from "../../style" 7 | import I18n from '../../style/i18n' 8 | import * as Constant from '../../style/constant' 9 | import Icon from 'react-native-vector-icons/FontAwesome' 10 | import Autolink from 'react-native-autolink'; 11 | import Toast from '../common/ToastProxy' 12 | import {launchUrl} from '../../utils/htmlUtils' 13 | 14 | 15 | class IconTextAutoLinkItem extends Component { 16 | componentDidMount() { 17 | } 18 | 19 | componentWillUnmount() { 20 | 21 | } 22 | 23 | render() { 24 | let smallIconTextStyle = [styles.flexDirectionRowNotFlex, styles.centerH,]; 25 | let halfEdge = (this.props.icon) ? Constant.normalMarginEdge / 2 : 0; 26 | let iconView = (this.props.icon) ? 27 | 28 | : ; 29 | return ( 30 | 32 | {iconView} 33 | { 37 | if (link && (link.indexOf("http") === 0 || link.indexOf("www") === 0)) { 38 | launchUrl(link); 39 | } else { 40 | Linking.openURL(link) 41 | } 42 | }} 43 | onLongPress={(link) => { 44 | Clipboard.setString(link); 45 | Toast(I18n("hadCopy")); 46 | }} 47 | style={[{marginLeft: halfEdge}, styles.miLightSmallText]} 48 | linkStyle={[ 49 | ...this.props.textstyle]}> 50 | 51 | 52 | ) 53 | } 54 | } 55 | 56 | 57 | IconTextAutoLinkItem.propTypes = { 58 | text: PropTypes.string, 59 | icon: PropTypes.string, 60 | textstyle: PropTypes.any, 61 | viewstyle: PropTypes.any, 62 | }; 63 | 64 | 65 | IconTextAutoLinkItem.defaultProps = { 66 | textstyle: [], 67 | viewstyle: [], 68 | }; 69 | 70 | export default IconTextAutoLinkItem; -------------------------------------------------------------------------------- /app/components/widget/IconTextItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text 4 | } from 'react-native'; 5 | import PropTypes from 'prop-types'; 6 | import styles from "../../style" 7 | import * as Constant from '../../style/constant' 8 | import Icon from 'react-native-vector-icons/FontAwesome' 9 | 10 | class IconTextItem extends Component { 11 | componentDidMount() { 12 | } 13 | 14 | componentWillUnmount() { 15 | 16 | } 17 | 18 | render() { 19 | let smallIconTextStyle = [styles.flexDirectionRowNotFlex, styles.centerH]; 20 | let halfEdge = (this.props.icon) ? Constant.normalMarginEdge / 2 : 0; 21 | let iconView = (this.props.icon) ? 22 | 23 | : ; 24 | return ( 25 | 27 | {iconView} 28 | 31 | {this.props.text} 32 | 33 | 34 | ) 35 | } 36 | } 37 | 38 | 39 | IconTextItem.propTypes = { 40 | text: PropTypes.string, 41 | icon: PropTypes.string, 42 | iconColor: PropTypes.string, 43 | iconSize: PropTypes.number, 44 | textstyle: PropTypes.any, 45 | viewstyle: PropTypes.any, 46 | }; 47 | 48 | 49 | IconTextItem.defaultProps = { 50 | textstyle: [], 51 | viewstyle: [], 52 | iconColor: Constant.miWhite, 53 | iconSize: Constant.littleIconSize, 54 | }; 55 | 56 | export default IconTextItem; -------------------------------------------------------------------------------- /app/components/widget/OrgItemBar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableOpacity, StyleSheet 4 | } from 'react-native'; 5 | import PropTypes from 'prop-types'; 6 | import styles from "../../style/index" 7 | import * as Constant from "../../style/constant" 8 | import IconC from 'react-native-vector-icons/Ionicons' 9 | import UserImage from "./UserImage"; 10 | import {Actions} from 'react-native-router-flux'; 11 | import I18n from '../../style/i18n' 12 | 13 | /** 14 | * 组织bar 15 | */ 16 | 17 | const itemSize = 35; 18 | 19 | class OrgItemBar extends Component { 20 | 21 | constructor(props) { 22 | super(props); 23 | this.renderItem = this.renderItem.bind(this); 24 | } 25 | 26 | componentDidMount() { 27 | } 28 | 29 | componentWillUnmount() { 30 | 31 | } 32 | 33 | renderItem(data) { 34 | return ( 35 | { 37 | data.itemClick && data.itemClick(data); 38 | }} 39 | key={data.login}> 40 | 47 | 48 | ) 49 | } 50 | 51 | render() { 52 | let {dataList} = this.props; 53 | let items = []; 54 | if (dataList.length > 0) { 55 | let title = 56 | 62 | 63 | {I18n("userOrg")} 64 | 65 | ; 66 | items.push(title) 67 | } 68 | dataList.forEach((data) => { 69 | items.push(this.renderItem(data)); 70 | }); 71 | let more = 72 | { 82 | Actions.ListPage({ 83 | dataType: 'user_orgs', showType: 'org', 84 | currentUser: this.props.ownerName, 85 | title: this.props.ownerName + " - " + I18n("userOrg") 86 | }) 87 | }}> 88 | 92 | ; 93 | if (dataList.length > 5) { 94 | items = items.slice(0, 4); 95 | items.push(more) 96 | } 97 | return ( 98 | 99 | 103 | {items} 104 | 105 | 106 | ) 107 | } 108 | } 109 | 110 | 111 | OrgItemBar.propTypes = { 112 | dataList: PropTypes.array, 113 | rootStyles: PropTypes.any, 114 | inputView: PropTypes.any, 115 | ownerName: PropTypes.string, 116 | }; 117 | 118 | 119 | OrgItemBar.defaultProps = { 120 | dataList: [], 121 | rootStyles: {} 122 | }; 123 | 124 | export default OrgItemBar -------------------------------------------------------------------------------- /app/components/widget/ReleaseItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | import React, { 5 | Component, 6 | } from 'react' 7 | import { 8 | View, Text, Image, TouchableOpacity 9 | } from 'react-native'; 10 | import PropTypes from 'prop-types'; 11 | import styles from '../../style' 12 | import * as Constant from '../../style/constant' 13 | import TimeText from './TimeText' 14 | import HTMLView from '../common/CommonHtmlView'; 15 | 16 | /** 17 | * 发布列表Item 18 | */ 19 | class ReleaseItem extends Component { 20 | constructor(props) { 21 | super(props) 22 | } 23 | 24 | componentDidMount() { 25 | } 26 | 27 | componentWillUnmount() { 28 | 29 | } 30 | 31 | render() { 32 | let {actionTime, actionTitle, actionUserPic, actionTargetHtml, actionTarget} = this.props; 33 | 34 | let body = (actionTargetHtml) ? { 48 | return ( 49 | 50 | ) 51 | }} 52 | /> : {actionTargetHtml}; 53 | return ( 54 | { 63 | this.props.onLongPressItem && this.props.onLongPressItem(); 64 | }} 65 | onPress={() => { 66 | this.props.onPressItem && this.props.onPressItem(); 67 | }}> 68 | 69 | 70 | 74 | {actionTitle} 75 | 76 | 79 | 80 | 81 | {body} 82 | 83 | ) 84 | } 85 | } 86 | 87 | const propTypes = { 88 | actionTime: PropTypes.string, 89 | actionTitle: PropTypes.string, 90 | actionMode: PropTypes.string, 91 | actionTarget: PropTypes.string, 92 | onPressItem: PropTypes.func, 93 | onLongPressItem: PropTypes.func, 94 | }; 95 | 96 | ReleaseItem.propTypes = propTypes; 97 | 98 | export default ReleaseItem -------------------------------------------------------------------------------- /app/components/widget/RepositoryPulseItem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/12. 3 | */ 4 | import React, { 5 | Component, 6 | } from 'react'; 7 | import PropTypes from 'prop-types'; 8 | import { 9 | Text, 10 | Image, 11 | View, 12 | TouchableOpacity, 13 | ImageBackground, 14 | StyleSheet, 15 | Platform 16 | } from 'react-native'; 17 | 18 | import * as Constant from '../../style/constant' 19 | import {Actions} from 'react-native-router-flux' 20 | import styles from '../../style' 21 | import Icon from 'react-native-vector-icons/FontAwesome' 22 | import IconC from 'react-native-vector-icons/Octicons' 23 | import UserImage from './UserImage' 24 | import IconTextItem from './IconTextItem' 25 | import HTMLView from '../common/CommonHtmlView'; 26 | import I18n from '../../style/i18n' 27 | import TagGroup from "./TagGroup"; 28 | 29 | /** 30 | * 仓库PulseItem显示 31 | */ 32 | class RepositoryPulseItem extends Component { 33 | constructor(props) { 34 | super(props) 35 | } 36 | 37 | componentDidMount() { 38 | } 39 | 40 | componentWillUnmount() { 41 | 42 | } 43 | 44 | render() { 45 | 46 | let bottomIconStyle = { 47 | backgroundColor: Constant.transparentColor, 48 | size: 22, 49 | }; 50 | 51 | let {opened, closed, infoText, statusText} = this.props; 52 | 53 | return ( 54 | 60 | 65 | 69 | 70 | 71 | {I18n("weekClosed") + closed} 73 | 74 | 77 | 78 | 79 | {I18n("weekOpened") + opened} 81 | 82 | 83 | {I18n("thisWeek")} 85 | { 100 | return ( 101 | 102 | ) 103 | }} 104 | /> 105 | 106 | ); 107 | } 108 | } 109 | 110 | const propTypes = {}; 111 | 112 | RepositoryPulseItem.propTypes = propTypes; 113 | 114 | RepositoryPulseItem.defaultProps = {}; 115 | 116 | 117 | export default RepositoryPulseItem; -------------------------------------------------------------------------------- /app/components/widget/SearchDrawerFilter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {DeviceEventEmitter, Text, View, ViewPropTypes,} from 'react-native'; 4 | import {Router, Actions, Scene} from 'react-native-router-flux'; 5 | import styles, {statusHeight, drawerWidth} from "../../style" 6 | import * as Constant from '../../style/constant' 7 | import {SortType, SearchFilterType, SearchLanguageType} from '../../utils/filterUtils' 8 | import SelectList from './SearchFilterSelectList' 9 | 10 | class SearchDrawerFilter extends React.Component { 11 | static propTypes = { 12 | name: PropTypes.string, 13 | sceneStyle: ViewPropTypes.style, 14 | title: PropTypes.string, 15 | }; 16 | 17 | static contextTypes = { 18 | drawer: PropTypes.object, 19 | }; 20 | 21 | render() { 22 | return ( 23 | 26 | 27 | 29 | 30 | { 39 | switch (selection) { 40 | case "filerType": 41 | Actions.pop({refresh: {selectTypeData: data}}); 42 | DeviceEventEmitter.emit("SearchPage", {selectTypeData: data}) 43 | break; 44 | case "filterLanguage": 45 | Actions.pop({refresh: {selectLanguageData: data}}); 46 | DeviceEventEmitter.emit("SearchPage", {selectLanguageData: data}) 47 | break; 48 | case "filterSort": 49 | Actions.pop({refresh: {selectSortData: data}}); 50 | DeviceEventEmitter.emit("SearchPage", {selectSortData: data}) 51 | break; 52 | } 53 | 54 | }} 55 | /> 56 | 57 | ); 58 | } 59 | } 60 | 61 | export default SearchDrawerFilter; 62 | -------------------------------------------------------------------------------- /app/components/widget/SearchFilterSelectList.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, 4 | Text, 5 | SectionList, 6 | TouchableOpacity 7 | } from 'react-native'; 8 | import PropTypes from 'prop-types'; 9 | import * as Constant from '../../style/constant' 10 | import styles from '../../style' 11 | import I18n from '../../style/i18n' 12 | 13 | class SearchFilterSelectList extends Component { 14 | state: { dataSource: any }; 15 | 16 | constructor(props: any) { 17 | super(props); 18 | this.state = { 19 | dataSource: this.props.selectMap, 20 | selectIndex: this.props.selectIndex 21 | }; 22 | 23 | this._renderRow = this._renderRow.bind(this); 24 | this._renderSectionHeader = this._renderSectionHeader.bind(this); 25 | } 26 | 27 | _renderRow({ item, index, section: { title, data } }) { 28 | return ( 29 | { 40 | let selectMap = this.props.selectMap; 41 | let dataList = data; 42 | dataList.forEach((data) => { 43 | data.select = false; 44 | }); 45 | dataList[data.indexOf(item)].select = true; 46 | 47 | this.setState({ 48 | dataSource: selectMap, 49 | }); 50 | this.props.onSelect && this.props.onSelect(title, dataList[index].value); 51 | }} 52 | > 53 | {I18n(item.name)} 55 | 56 | ); 57 | } 58 | 59 | _renderSectionHeader({section: {title}}) { 60 | return ( 61 | 71 | {I18n(title)} 72 | 73 | ) 74 | } 75 | 76 | render() { 77 | return ( 78 | item + index} 84 | /> 85 | ); 86 | } 87 | } 88 | 89 | 90 | const propTypes = { 91 | selectMap: PropTypes.any, 92 | selectIndex: PropTypes.any, 93 | listStyle: PropTypes.any, 94 | onSelect: PropTypes.func, 95 | }; 96 | 97 | SearchFilterSelectList.propTypes = propTypes; 98 | 99 | SearchFilterSelectList.defaultProps = { 100 | selectMap: {}, 101 | selectIndex: {}, 102 | listStyle: {}, 103 | }; 104 | 105 | export default SearchFilterSelectList; -------------------------------------------------------------------------------- /app/components/widget/TabIcon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/2/10. 3 | */ 4 | 5 | import React, { 6 | Component, 7 | } from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import { 10 | Text, 11 | Image, 12 | View, 13 | StyleSheet, 14 | TouchableOpacity, 15 | Dimensions, 16 | } from 'react-native'; 17 | 18 | import * as Constant from '../../style/constant' 19 | import styles from '../../style' 20 | import I18n from '../../style/i18n' 21 | import Icon from 'react-native-vector-icons/Feather' 22 | 23 | 24 | const config = { 25 | ["tabRecommended"]: 'activity', 26 | ["tabDynamic"]: 'aperture', 27 | ["tabMy"]: 'users', 28 | }; 29 | 30 | const propTypes = { 31 | focused: PropTypes.bool, 32 | title: PropTypes.string, 33 | tabName: PropTypes.string, 34 | tabIconName: PropTypes.string, 35 | }; 36 | 37 | /** 38 | * 底部Tab 39 | */ 40 | class TabIcon extends Component { 41 | 42 | constructor(props) { 43 | super(props) 44 | } 45 | 46 | render() { 47 | 48 | let iconPath = config[this.props.tabIconName]; 49 | 50 | let color = this.props.focused ? Constant.tabSelectedColor : Constant.tabUnSelectColor; 51 | 52 | return ( 53 | 54 | 55 | {this.props.title} 56 | 57 | ); 58 | } 59 | } 60 | 61 | TabIcon.propTypes = propTypes; 62 | 63 | export default TabIcon; -------------------------------------------------------------------------------- /app/components/widget/TagGroup.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | } from 'react'; 4 | import { 5 | Text, 6 | View, 7 | TouchableOpacity, 8 | } from 'react-native'; 9 | import PropTypes from 'prop-types'; 10 | import styles from "../../style/index" 11 | import * as Constant from "../../style/constant" 12 | import {Actions} from "react-native-router-flux"; 13 | 14 | /** 15 | * 标签控件 16 | */ 17 | class TagGroup extends Component { 18 | 19 | constructor(props) { 20 | super(props); 21 | 22 | } 23 | 24 | render() { 25 | if (!this.props.tagList || this.props.tagList.length === 0) { 26 | return 27 | } 28 | return ( 29 | 30 | { 31 | this.props.tagList.map((data) => { 32 | return ( 33 | { 35 | Actions.ListPage({ 36 | dataType: 'topics', showType: 'repository', 37 | title: data, 38 | topic: data 39 | }) 40 | }} 41 | key={"_" + data} 42 | style={[styles.centered, { 43 | borderRadius: 4, 44 | height: 24, 45 | overflow: 'hidden', 46 | paddingHorizontal: Constant.normalMarginEdge, 47 | backgroundColor: Constant.white, 48 | marginVertical: Constant.normalMarginEdge / 2, 49 | marginRight: Constant.normalMarginEdge, 50 | }]}> 51 | 53 | {data} 54 | 55 | 56 | ) 57 | 58 | }) 59 | } 60 | 61 | ); 62 | } 63 | 64 | 65 | } 66 | 67 | TagGroup.propTypes = { 68 | groupStyle: PropTypes.any, 69 | tagList: PropTypes.array, 70 | }; 71 | 72 | export default TagGroup; -------------------------------------------------------------------------------- /app/components/widget/TimeText.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import {View, Image, Text, StyleSheet} from 'react-native' 7 | import PropTypes from 'prop-types'; 8 | import resolveTime from '../../utils/timeUtil' 9 | 10 | const propTypes = { 11 | time: PropTypes.any, 12 | }; 13 | 14 | /** 15 | * 时间显示控件 16 | */ 17 | class Time extends Component { 18 | 19 | getTimeText() { 20 | let {time} = this.props; 21 | return resolveTime(time) 22 | } 23 | 24 | render() { 25 | let time = this.getTimeText(); 26 | return ( 27 | {time} 28 | ) 29 | } 30 | } 31 | 32 | 33 | Time.propTypes = propTypes; 34 | 35 | export default Time; 36 | -------------------------------------------------------------------------------- /app/components/widget/TrendPickerItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | View, Text, TouchableHighlight, StyleSheet 4 | } from 'react-native'; 5 | import styles, {screenWidth} from "../../style" 6 | import * as Constant from "../../style/constant" 7 | import I18n from '../../style/i18n' 8 | import ModalDropdown from '../common/ModalDropdown'; 9 | import PropTypes from 'prop-types'; 10 | import Icon from 'react-native-vector-icons/Ionicons' 11 | 12 | /** 13 | * 趋势数据过滤Item 14 | */ 15 | class TrendPickerItem extends Component { 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | icon:'md-arrow-dropdown' 21 | } 22 | } 23 | 24 | componentDidMount() { 25 | 26 | } 27 | 28 | componentWillUnmount() { 29 | } 30 | 31 | render() { 32 | return ( 33 | 34 | { 41 | this.props.onSelect && this.props.onSelect(rowID, rowData); 42 | return true 43 | }} 44 | defaultIndex={this.props.defaultIndex} 45 | defaultValue={this.props.defaultValue} 46 | onDropdownWillShow={()=>{ 47 | this.setState({ 48 | icon:'md-arrow-dropup' 49 | }) 50 | }} 51 | onDropdownWillHide={()=>{ 52 | this.setState({ 53 | icon:'md-arrow-dropdown' 54 | }) 55 | }} 56 | renderRow={this.renderRow.bind(this)} 57 | renderSeparator={(sectionID, rowID, adjacentRowHighlighted) => this.renderSeparator(sectionID, rowID, adjacentRowHighlighted)} 58 | /> 59 | 67 | 68 | 69 | 70 | ); 71 | } 72 | 73 | renderRow(rowData, rowID, highlighted) { 74 | let evenRow = rowID % 2; 75 | return ( 76 | 77 | 84 | 88 | {I18n(rowData.name)} 89 | 90 | 91 | 92 | ); 93 | } 94 | 95 | renderSeparator(sectionID, rowID, adjacentRowHighlighted) { 96 | if (rowID === this.props.options.length - 1) return; 97 | let key = `spr_${rowID}`; 98 | return ( 99 | 101 | ); 102 | } 103 | } 104 | 105 | TrendPickerItem.propTypes = { 106 | itemHeight: PropTypes.number, 107 | defaultIndex: PropTypes.number, 108 | defaultValue: PropTypes.string, 109 | options: PropTypes.array, 110 | dropdownStyle: PropTypes.any, 111 | textStyle: PropTypes.any, 112 | adjustFrame: PropTypes.any, 113 | onSelect: PropTypes.func, 114 | }; 115 | 116 | 117 | export default TrendPickerItem 118 | -------------------------------------------------------------------------------- /app/components/widget/UserImage.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | } from 'react' 4 | import { 5 | View, Text, Image, TouchableWithoutFeedback 6 | } from 'react-native'; 7 | import PropTypes from 'prop-types'; 8 | import {Actions} from 'react-native-router-flux'; 9 | 10 | /** 11 | * 用户头像 12 | */ 13 | export default class UserImage extends Component { 14 | 15 | render() { 16 | return ( 17 | { 18 | Actions.PersonPage({currentUser: this.props.loginUser}) 19 | }}> 20 | 23 | 24 | ) 25 | } 26 | } 27 | 28 | UserImage.propTypes = { 29 | loginUser: PropTypes.string, 30 | uri: PropTypes.string, 31 | }; -------------------------------------------------------------------------------- /app/components/widget/UserItem.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | } from 'react' 4 | import { 5 | View, Text, Image, TouchableOpacity 6 | } from 'react-native'; 7 | import PropTypes from 'prop-types'; 8 | import styles from '../../style' 9 | import * as Constant from '../../style/constant' 10 | import {Actions} from 'react-native-router-flux'; 11 | import UserImage from './UserImage' 12 | 13 | /** 14 | * 用户列表Item 15 | */ 16 | class UserItem extends Component { 17 | constructor(props) { 18 | super(props) 19 | } 20 | 21 | componentDidMount() { 22 | } 23 | 24 | componentWillUnmount() { 25 | 26 | } 27 | 28 | render() { 29 | let {location, actionUser, actionUserPic} = this.props; 30 | let bottomDes = (this.props.des) ? 31 | 34 | {this.props.des} 35 | : ; 36 | return ( 37 | { 46 | Actions.PersonPage({currentUser: actionUser}) 47 | }}> 48 | 49 | 57 | 58 | 62 | {actionUser} 63 | 64 | 66 | {location} 67 | 68 | 69 | 70 | {bottomDes} 71 | 72 | ) 73 | } 74 | } 75 | 76 | const propTypes = { 77 | location: PropTypes.string, 78 | actionUser: PropTypes.string, 79 | actionUserPic: PropTypes.string, 80 | des: PropTypes.string, 81 | }; 82 | 83 | UserItem.propTypes = propTypes; 84 | 85 | export default UserItem -------------------------------------------------------------------------------- /app/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/15. 3 | */ 4 | 5 | /*** 6 | * 你需要自己在config目录下 创建一个ignoreConfig.js文件 7 | * 然后输入你的client_id 和 client_secret 8 | * export const CLIENT_ID = "xxxx"; 9 | * export const CLIENT_SECRET = "xxxx"; 10 | * //如果需要上传七牛 11 | * export const ACCESS_KEY = "xxxx"; 12 | * export const SECRET_KEY = "xxx"; 13 | * export const QN_HOST = "xxxx"; 14 | * export const SCOPE = "xxxx"; 15 | */ 16 | 17 | export const PAGE_SIZE = 30; -------------------------------------------------------------------------------- /app/img/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/app/img/default_img.png -------------------------------------------------------------------------------- /app/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/app/img/logo.png -------------------------------------------------------------------------------- /app/img/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/app/img/welcome.png -------------------------------------------------------------------------------- /app/net/netwrokCode.js: -------------------------------------------------------------------------------- 1 | import {Actions} from "react-native-router-flux"; 2 | import I18n from '../style/i18n' 3 | import Toast from '../components/common/ToastProxy' 4 | 5 | //网络错误 6 | export const NETWORK_ERROR = 1; 7 | //网络超时 8 | export const NETWORK_TIMEOUT = 2; 9 | //网络返回数据格式化一次 10 | export const NETWORK_JSON_EXCEPTION = 3; 11 | 12 | 13 | export const SUCCESS = 200; 14 | 15 | 16 | export default function (code, statusText) { 17 | switch (code) { 18 | case 401: 19 | //授权逻辑 20 | if (Actions.currentScene !== 'LoginPage') { 21 | Actions.reset("LoginPage"); 22 | } 23 | return "未授权或授权失败";//401 Unauthorized 24 | case 403: 25 | Toast(I18n('noPower')); 26 | return "403权限错误"; 27 | case 404: 28 | //Toast(I18n('notFound')); 29 | return "404错误"; 30 | case 410: 31 | Toast(I18n('gone410')); 32 | return "410错误"; 33 | case NETWORK_TIMEOUT: 34 | //超时 35 | Toast(I18n('netTimeout')); 36 | return I18n('netTimeout'); 37 | default: 38 | if (statusText) { 39 | Toast(statusText); 40 | } else { 41 | Toast(I18n('errorUnKnow')); 42 | } 43 | return "其他异常" 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /app/net/qiniu/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by buhe on 16/4/12. 3 | */ 4 | import base64 from 'base-64'; 5 | import CryptoJS from "crypto-js"; 6 | import conf from "./conf.js"; 7 | import parse from 'url-parse'; 8 | 9 | function urlsafeBase64Encode(jsonFlags) { 10 | var encoded = base64.encode(jsonFlags); 11 | return base64ToUrlSafe(encoded); 12 | }; 13 | 14 | function base64ToUrlSafe(v) { 15 | return v.replace(/\//g, '_').replace(/\+/g, '-'); 16 | }; 17 | 18 | function hmacSha1(encodedFlags, secretKey) { 19 | var encoded = CryptoJS.HmacSHA1(encodedFlags, secretKey).toString(CryptoJS.enc.Base64); 20 | return encoded; 21 | }; 22 | 23 | function generateAccessToken(url, body) { 24 | var u = parse(url, true); 25 | 26 | var path = u.pathname; 27 | var access = path + '\n'; 28 | 29 | if (body) { 30 | access += body; 31 | } 32 | 33 | var digest = hmacSha1(access, conf.SECRET_KEY); 34 | var safeDigest = base64ToUrlSafe(digest); 35 | let token = 'QBox ' + conf.ACCESS_KEY + ':' + safeDigest; 36 | //console.log(token); 37 | return token; 38 | }; 39 | 40 | class PutPolicy2 { 41 | constructor(putPolicyObj) { 42 | if (typeof putPolicyObj !== 'object') { 43 | return false; 44 | } 45 | 46 | this.scope = putPolicyObj.scope || null; 47 | this.expires = putPolicyObj.expires || 3600; 48 | this.insertOnly = putPolicyObj.insertOnly || null; 49 | 50 | this.saveKey = putPolicyObj.saveKey || null; 51 | this.endUser = putPolicyObj.endUser || null; 52 | 53 | this.returnUrl = putPolicyObj.returnUrl || null; 54 | this.returnBody = putPolicyObj.returnBody || null; 55 | 56 | this.callbackUrl = putPolicyObj.callbackUrl || null; 57 | this.callbackHost = putPolicyObj.callbackHost || null; 58 | this.callbackBody = putPolicyObj.callbackBody || null; 59 | this.callbackBodyType = putPolicyObj.callbackBodyType || null; 60 | 61 | this.persistentOps = putPolicyObj.persistentOps || null; 62 | this.persistentNotifyUrl = putPolicyObj.persistentNotifyUrl || null; 63 | this.persistentPipeline = putPolicyObj.persistentPipeline || null; 64 | 65 | this.fsizeLimit = putPolicyObj.fsizeLimit || null; 66 | 67 | this.fsizeMin = putPolicyObj.fsizeMin || null; 68 | 69 | this.detectMime = putPolicyObj.detectMime || null; 70 | 71 | this.mimeLimit = putPolicyObj.mimeLimit || null; 72 | } 73 | 74 | token() { 75 | var flags = this.getFlags(); 76 | var encodedFlags = urlsafeBase64Encode(JSON.stringify(flags)); 77 | var encoded = hmacSha1(encodedFlags, conf.SECRET_KEY); 78 | var encodedSign = base64ToUrlSafe(encoded); 79 | var uploadToken = conf.ACCESS_KEY + ':' + encodedSign + ':' + encodedFlags; 80 | return uploadToken; 81 | } 82 | 83 | getFlags() { 84 | var flags = {}; 85 | var attrs = ['scope', 'insertOnly', 'saveKey', 'endUser', 'returnUrl', 'returnBody', 'callbackUrl', 'callbackHost', 'callbackBody', 'callbackBodyType', 'callbackFetchKey', 'persistentOps', 'persistentNotifyUrl', 'persistentPipeline', 'fsizeLimit', 'fsizeMin', 'detectMime', 'mimeLimit']; 86 | 87 | for (var i = attrs.length - 1; i >= 0; i--) { 88 | if (this[attrs[i]] !== null) { 89 | flags[attrs[i]] = this[attrs[i]]; 90 | } 91 | } 92 | 93 | flags['deadline'] = this.expires + Math.floor(Date.now() / 1000); 94 | 95 | return flags; 96 | } 97 | } 98 | 99 | class GetPolicy { 100 | constructor(expires) { 101 | this.expires = expires || 3600; 102 | } 103 | 104 | makeRequest(baseUrl) { 105 | var deadline = this.expires + Math.floor(Date.now() / 1000); 106 | 107 | if (baseUrl.indexOf('?') >= 0) { 108 | baseUrl += '&e='; 109 | } else { 110 | baseUrl += '?e='; 111 | } 112 | baseUrl += deadline; 113 | 114 | var signature = hmacSha1(baseUrl, conf.SECRET_KEY); 115 | var encodedSign = base64ToUrlSafe(signature); 116 | var downloadToken = conf.ACCESS_KEY + ':' + encodedSign; 117 | 118 | return baseUrl + '&token=' + downloadToken; 119 | } 120 | } 121 | 122 | export default {urlsafeBase64Encode,generateAccessToken,PutPolicy2,GetPolicy} 123 | -------------------------------------------------------------------------------- /app/net/qiniu/conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by buhe on 16/4/12. 3 | */ 4 | let ACCESS_KEY = ''; 5 | let SECRET_KEY = ''; 6 | 7 | let UP_HOST = 'https://up-z2.qiniup.com'; 8 | let RS_HOST = 'http://rs.qbox.me'; 9 | let RSF_HOST = 'http://rsf.qbox.me'; 10 | let API_HOST = 'http://api.qiniu.com'; 11 | let RPC_TIMEOUT = 3600000; // default rpc timeout: one hour 12 | 13 | export default { 14 | ACCESS_KEY,SECRET_KEY,UP_HOST,RS_HOST,RSF_HOST,API_HOST,RPC_TIMEOUT 15 | } -------------------------------------------------------------------------------- /app/net/qiniu/imageOps.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by buhe on 16/4/12. 3 | */ 4 | import util from './auth'; 5 | import rpc from './rpc'; 6 | import conf from './conf'; 7 | 8 | class ImageView { 9 | constructor(mode, width, height, quality, format) { 10 | this.mode = mode || 1; 11 | this.width = width || 0; 12 | this.height = height || 0; 13 | this.quality = quality || 0; 14 | this.format = format || null; 15 | } 16 | 17 | makeRequest(url) { 18 | url += '?imageView2/' + this.mode; 19 | 20 | if (this.width > 0) { 21 | url += '/w/' + this.width; 22 | } 23 | 24 | if (this.height > 0) { 25 | url += '/h/' + this.height; 26 | } 27 | 28 | if (this.quality > 0) { 29 | url += '/q/' + this.quality; 30 | } 31 | 32 | if (this.format) { 33 | url += '/format/' + this.format; 34 | } 35 | 36 | return url; 37 | } 38 | } 39 | 40 | class ImageInfo { 41 | makeRequest(url) { 42 | return url + '?imageInfo' 43 | } 44 | } 45 | 46 | class Exif { 47 | makeRequest(url) { 48 | return url + '?exif' 49 | } 50 | } 51 | 52 | export default {ImageView,ImageInfo,Exif} 53 | -------------------------------------------------------------------------------- /app/net/qiniu/index.js: -------------------------------------------------------------------------------- 1 | import Rpc from "./rpc" 2 | import Auth from "./auth" 3 | import Conf from './conf.js'; 4 | import moment from 'moment'; 5 | import {ACCESS_KEY, SECRET_KEY, QN_HOST, SCOPE} from "../../config/ignoreConfig" 6 | 7 | Conf.ACCESS_KEY = ACCESS_KEY; 8 | Conf.SECRET_KEY = SECRET_KEY; 9 | 10 | /** 11 | * 上传图片到七牛 12 | */ 13 | export const uploadQiNiu = async (base64) => { 14 | let putPolicy = new Auth.PutPolicy2( 15 | {scope: SCOPE} 16 | ); 17 | let uptoken = putPolicy.token(); 18 | let formInput = { 19 | key: moment().format('YYYY-MM-DD') + "/" + moment().toDate().getTime() + ".jpg", 20 | }; 21 | let res = await Rpc.uploadBase64(base64, uptoken, formInput.key); 22 | if (res.status === 200) { 23 | return { 24 | result: true, 25 | data: QN_HOST + formInput.key 26 | } 27 | } else { 28 | return { 29 | result: false, 30 | data: "" 31 | } 32 | } 33 | 34 | }; -------------------------------------------------------------------------------- /app/net/qiniu/rpc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by buhe on 16/4/12. 3 | */ 4 | import conf from './conf.js'; 5 | import auth from './auth'; 6 | 7 | function uploadBase64(base64, token, strKey) { 8 | let key = auth.urlsafeBase64Encode(strKey); 9 | return new Promise((resolve, reject) => { 10 | if (typeof base64 !== 'string' || base64 === '' || typeof key === 'undefined') { 11 | reject && reject(null); 12 | return; 13 | } 14 | let xhr = new XMLHttpRequest(); 15 | xhr.open('POST', conf.UP_HOST + "/putb64/-1/key/" + key, true); 16 | xhr.setRequestHeader("Authorization", "UpToken " + token); 17 | xhr.setRequestHeader("Content-Type", "application/octet-stream"); 18 | xhr.onreadystatechange = (e) => { 19 | if (__DEV__) { 20 | console.log("upload state " + xhr.readyState); 21 | console.log("upload status " + xhr.status); 22 | console.log("upload _response " + xhr._response); 23 | } 24 | if (xhr.readyState !== 4) { 25 | return; 26 | } 27 | if (xhr.status !== 200) { 28 | reject && reject(xhr); 29 | return; 30 | } 31 | resolve && resolve(xhr); 32 | }; 33 | xhr.send(base64); 34 | }); 35 | } 36 | 37 | /** 38 | * 直传文件 39 | * formInput对象如何配置请参考七牛官方文档“直传文件”一节 40 | */ 41 | function uploadFile(uri, token, formInput, onprogress) { 42 | return new Promise((resolve, reject) => { 43 | if (typeof uri !== 'string' || uri === '' || typeof formInput.key === 'undefined') { 44 | reject && reject(null); 45 | return; 46 | } 47 | if (uri[0] === '/') { 48 | uri = "file://" + uri; 49 | } 50 | let xhr = new XMLHttpRequest(); 51 | xhr.open('POST', conf.UP_HOST); 52 | xhr.onload = () => { 53 | if (xhr.status !== 200) { 54 | reject && reject(xhr); 55 | return; 56 | } 57 | 58 | resolve && resolve(xhr); 59 | }; 60 | 61 | var formdata = new FormData(); 62 | formdata.append("key", formInput.key); 63 | formdata.append("token", token); 64 | if (typeof formInput.type == 'undefined') 65 | formInput.type = 'application/octet-stream'; 66 | if (typeof formInput.name == 'undefined') { 67 | var filePath = uri.split("/"); 68 | if (filePath.length > 0) 69 | formInput.name = filePath[filePath.length - 1]; 70 | else 71 | formInput.name = ""; 72 | } 73 | formdata.append("file", {uri: uri, type: formInput.type, name: formInput.name}); 74 | xhr.upload.onprogress = (event) => { 75 | onprogress && onprogress(event, xhr); 76 | }; 77 | xhr.send(formdata); 78 | }); 79 | } 80 | 81 | //发送管理和fop命令,总之就是不上传文件 82 | function post(uri, adminToken, content) { 83 | var headers = { 84 | 'Content-Type': 'application/x-www-form-urlencoded', 85 | }; 86 | let payload = { 87 | headers: headers, 88 | method: 'POST', 89 | dataType: 'json', 90 | timeout: conf.RPC_TIMEOUT, 91 | }; 92 | if (typeof content === 'undefined') { 93 | payload.headers['Content-Length'] = 0; 94 | } else { 95 | //carry data 96 | payload.body = content; 97 | } 98 | 99 | if (adminToken) { 100 | headers['Authorization'] = adminToken; 101 | } 102 | 103 | return fetch(uri, payload); 104 | } 105 | 106 | export default {uploadFile, post, uploadBase64} -------------------------------------------------------------------------------- /app/store/actions/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/16. 3 | */ 4 | 5 | import {EVENT} from '../type' 6 | import EventDao from '../../dao/eventDao' 7 | 8 | /** 9 | * 用户接收事件 10 | */ 11 | const getEventReceived = (page = 0, callback) => async (dispatch, getState) => { 12 | let user = getState()['user']; 13 | if (!user || !user.userInfo || !user.userInfo.login) { 14 | callback && callback(null); 15 | return; 16 | } 17 | if (page <= 1) { 18 | let resLocal = await EventDao.getEventReceivedDao(page, user.userInfo.login, true); 19 | if (resLocal && resLocal.result) { 20 | dispatch({ 21 | type: EVENT.RECEIVED_EVENTS, 22 | res: resLocal.data 23 | }); 24 | } 25 | } 26 | let res = await EventDao.getEventReceivedDao(page, user.userInfo.login); 27 | if (res && res.result) { 28 | if (page === 0) { 29 | dispatch({ 30 | type: EVENT.RECEIVED_EVENTS, 31 | res: res.data 32 | }); 33 | } else { 34 | let event = getState()['event'].received_events_data_list; 35 | dispatch({ 36 | type: EVENT.RECEIVED_EVENTS, 37 | res: event.concat(res.data) 38 | }); 39 | } 40 | callback && callback(res.data); 41 | } else { 42 | callback && callback(null); 43 | } 44 | }; 45 | 46 | /** 47 | * 用户行为事件 48 | */ 49 | const getEvent = async (page = 0, userName) => { 50 | if (!userName) { 51 | return null; 52 | } 53 | if (page <= 1) { 54 | return EventDao.getEventDao(page, userName, true); 55 | } 56 | return EventDao.getEventDao(page, userName); 57 | }; 58 | 59 | /** 60 | * 仓库活动事件 61 | */ 62 | const getRepositoryEvent = async (page = 0, userName, repository) => { 63 | if (page <= 1) { 64 | return EventDao.getRepositoryEventDao(page, userName, repository, true); 65 | } 66 | return await EventDao.getRepositoryEventDao(page, userName, repository); 67 | }; 68 | 69 | export default { 70 | getEventReceived, 71 | getEvent, 72 | getRepositoryEvent, 73 | } 74 | -------------------------------------------------------------------------------- /app/store/actions/issue.js: -------------------------------------------------------------------------------- 1 | import IssueDao from '../../dao/issueDao' 2 | 3 | /** 4 | * 获取仓库issue 5 | * @param page 6 | * @param userName 7 | * @param repository 8 | * @param state issue状态 9 | * @param sort 排序类型 created updated等 10 | * @param direction 正序或者倒序 11 | * @returns {Promise.} 12 | */ 13 | const getRepositoryIssue = async (page = 0, userName, repository, state, sort, direction) => { 14 | if (page <= 1) { 15 | return IssueDao.getRepositoryIssueDao(page, userName, repository, state, sort, direction, true); 16 | } 17 | return IssueDao.getRepositoryIssueDao(page, userName, repository, state, sort, direction); 18 | }; 19 | 20 | /** 21 | * issue的回复列表 22 | */ 23 | const getIssueComment = async (page = 0, userName, repository, number) => { 24 | if (page <= 1) { 25 | return IssueDao.getIssueCommentDao(page, userName, repository, number, true); 26 | } 27 | return IssueDao.getIssueCommentDao(page, userName, repository, number) 28 | 29 | }; 30 | 31 | /** 32 | * issue的详请 33 | */ 34 | const getIssueInfo = async (userName, repository, number) => { 35 | return IssueDao.getIssueInfoDao(userName, repository, number); 36 | }; 37 | 38 | /** 39 | * 增加issue的回复 40 | */ 41 | const addIssueComment = async (userName, repository, number, comment) => { 42 | let res = await IssueDao.addIssueCommentDao(userName, repository, number, comment); 43 | if (res && res.result) { 44 | return { 45 | data: res.data, 46 | result: true 47 | }; 48 | } else { 49 | return { 50 | result: false 51 | }; 52 | } 53 | }; 54 | 55 | /** 56 | * 编辑issue 57 | */ 58 | const editIssue = async (userName, repository, number, issue) => { 59 | let res = await IssueDao.editIssueDao(userName, repository, number, issue); 60 | if (res && res.result) { 61 | return { 62 | data: res.data, 63 | result: true 64 | }; 65 | } else { 66 | return { 67 | result: false 68 | }; 69 | } 70 | }; 71 | 72 | /** 73 | * 锁定issue 74 | */ 75 | const lockIssue = async (userName, repository, number, locked) => { 76 | let res = await IssueDao.lockIssueDao(userName, repository, number, locked); 77 | if (res && res.result) { 78 | return { 79 | data: res.data, 80 | result: true 81 | }; 82 | } else { 83 | return { 84 | result: false 85 | }; 86 | } 87 | }; 88 | 89 | 90 | /** 91 | * 创建issue 92 | */ 93 | const createIssue = async (userName, repository, issue) => { 94 | let res = await IssueDao.createIssueDao(userName, repository, issue); 95 | if (res && res.result) { 96 | return { 97 | data: res.data, 98 | result: true 99 | }; 100 | } else { 101 | return { 102 | result: false 103 | }; 104 | } 105 | }; 106 | 107 | /** 108 | * 编辑issue回复 109 | */ 110 | const editComment = async (userName, repository, number, commentId, comment, type) => { 111 | let res; 112 | if (type === 'delete') { 113 | res = await IssueDao.deleteCommentDao(userName, repository, number, commentId); 114 | } else { 115 | res = await IssueDao.editCommentDao(userName, repository, number, commentId, comment); 116 | } 117 | if (res && res.result) { 118 | return { 119 | data: res.data, 120 | result: true 121 | }; 122 | } else { 123 | return { 124 | result: false 125 | }; 126 | } 127 | }; 128 | 129 | 130 | export default { 131 | getRepositoryIssue, 132 | getIssueComment, 133 | getIssueInfo, 134 | addIssueComment, 135 | editIssue, 136 | editComment, 137 | createIssue, 138 | lockIssue 139 | } 140 | -------------------------------------------------------------------------------- /app/store/actions/login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | 5 | import Api from '../../net' 6 | import Address from '../../net/address' 7 | import {LOGIN} from '../type' 8 | import userAction from './user' 9 | import * as Constant from '../../style/constant' 10 | import {Buffer} from 'buffer' 11 | import {clear} from '../reducers' 12 | import {CLIENT_ID, CLIENT_SECRET} from '../../config/ignoreConfig' 13 | import AsyncStorage from '@react-native-community/async-storage'; 14 | 15 | const toLogin = () => async (dispatch, getState) => { 16 | 17 | }; 18 | 19 | /** 20 | * 登陆请求 21 | */ 22 | const doLogin = (code, callback) => async (dispatch, getState) => { 23 | Api.clearAuthorization(); 24 | 25 | let url = `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&code=${code}` 26 | let res = await Api.netFetch(url, 'GET', null, false, null, true); 27 | if (res && res.result) { 28 | let data = res.data; 29 | let key = "access_token="; 30 | let token = data.substring((data.indexOf(key) + key.length), data.indexOf("&")) 31 | let authorizationCode = 'token ' + token; 32 | await AsyncStorage.setItem(Constant.TOKEN_KEY, authorizationCode); 33 | let current = await userAction.getUserInfo(); 34 | dispatch({ 35 | type: LOGIN.IN, 36 | res 37 | }); 38 | } 39 | setTimeout(() => { 40 | callback && callback(res.result); 41 | }, 1000) 42 | }; 43 | 44 | /** 45 | * 退出登录 46 | */ 47 | const loginOut = () => async (dispatch, getState) => { 48 | Api.clearAuthorization(); 49 | AsyncStorage.removeItem(Constant.USER_BASIC_CODE); 50 | userAction.clearUserInfo(); 51 | clear(getState); 52 | dispatch({ 53 | type: LOGIN.CLEAR, 54 | }); 55 | }; 56 | 57 | /** 58 | * 获取当前登录用户的参数 59 | */ 60 | const getLoginParams = async () => { 61 | let userName = await AsyncStorage.getItem(Constant.USER_NAME_KEY); 62 | let password = await AsyncStorage.getItem(Constant.PW_KEY); 63 | return { 64 | userName: (userName) ? userName : "", 65 | password: (password) ? password : "", 66 | } 67 | }; 68 | 69 | export default { 70 | toLogin, 71 | doLogin, 72 | getLoginParams, 73 | loginOut 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/store/actions/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/9. 3 | */ 4 | 5 | import {USER} from '../type' 6 | import UserDao from '../../dao/userDao' 7 | import * as Constant from '../../style/constant' 8 | import store from '../' 9 | 10 | import AsyncStorage from '@react-native-community/async-storage'; 11 | 12 | const {dispatch, getState} = store; 13 | 14 | /** 15 | * 初始化用户信息 16 | */ 17 | const initUserInfo = async () => { 18 | let token = await AsyncStorage.getItem(Constant.TOKEN_KEY); 19 | let res = await UserDao.getUserInfoLocal(); 20 | if (res && res.result && token) { 21 | dispatch({ 22 | type: USER.USER_INFO, 23 | res: res.data 24 | }); 25 | } 26 | return { 27 | result: res.result && (token !== null), 28 | data: res.data 29 | }; 30 | 31 | }; 32 | 33 | /** 34 | * 获取登录用户信息 35 | */ 36 | const getUserInfo = async () => { 37 | let res = await UserDao.getUserInfoDao(); 38 | let resData = await res.next(); 39 | if (resData && resData.result) { 40 | dispatch({ 41 | type: USER.USER_INFO, 42 | res: resData.data 43 | }); 44 | } 45 | return resData; 46 | }; 47 | 48 | /** 49 | * 获取其他用户信息 50 | */ 51 | const getPersonUserInfo = async (userName) => { 52 | return UserDao.getUserInfoDao(userName); 53 | }; 54 | 55 | /** 56 | * 清除登录用户信息 57 | */ 58 | const clearUserInfo = () => { 59 | AsyncStorage.removeItem(Constant.USER_INFO); 60 | dispatch({ 61 | type: USER.USER_INFO, 62 | res: null 63 | }); 64 | }; 65 | 66 | /** 67 | * 获取用户粉丝列表 68 | */ 69 | const getFollowerList = async (userName, page = 1) => { 70 | if (page <= 1) { 71 | return UserDao.getFollowerListDao(userName, page, true); 72 | } else { 73 | return UserDao.getFollowerListDao(userName, page) 74 | } 75 | }; 76 | 77 | /** 78 | * 获取用户关注列表 79 | */ 80 | const getFollowedList = async (userName, page = 1) => { 81 | if (page <= 1) { 82 | return UserDao.getFollowedListDao(userName, page, true); 83 | } else { 84 | return UserDao.getFollowedListDao(userName, page) 85 | } 86 | }; 87 | 88 | /** 89 | * 获取用户相关通知 90 | */ 91 | const getNotifation = async (all, participating, page) => { 92 | let res = await UserDao.getNotifationDao(all, participating, page) 93 | return { 94 | result: res.result, 95 | data: res.data 96 | } 97 | }; 98 | 99 | /** 100 | * 设置单个通知已读 101 | */ 102 | const setNotificationAsRead = async (id) => { 103 | let res = await UserDao.setNotificationAsReadDao(id); 104 | return { 105 | result: res.result, 106 | data: res.data 107 | } 108 | }; 109 | 110 | /** 111 | * 设置所有通知已读 112 | */ 113 | const setAllNotificationAsRead = async () => { 114 | let res = await UserDao.setAllNotificationAsReadDao(); 115 | return { 116 | result: res.result, 117 | data: res.data 118 | } 119 | }; 120 | 121 | /** 122 | * 更新用户信息 123 | */ 124 | const updateUser = async (params) => { 125 | let res = await UserDao.updateUserDao(params); 126 | if (res && res.result) { 127 | dispatch({ 128 | type: USER.USER_INFO, 129 | res: res.data 130 | }); 131 | } 132 | return { 133 | result: res.result, 134 | data: res.data 135 | } 136 | }; 137 | 138 | /** 139 | * 关注用户 140 | */ 141 | const doFollow = async (name, followed) => { 142 | let res = await UserDao.doFollowDao(name, followed); 143 | return { 144 | result: res.result, 145 | data: res.data 146 | } 147 | }; 148 | 149 | /** 150 | * 检查用户关注状态 151 | */ 152 | const checkFollow = async (name, followed) => { 153 | let res = await UserDao.checkFollowDao(name); 154 | return { 155 | result: res.result, 156 | data: res.data 157 | } 158 | }; 159 | 160 | 161 | /** 162 | * 获取组织成员 163 | */ 164 | const getMember = async (page = 1, userName) => { 165 | if (page <= 1) { 166 | return UserDao.getMemberDao(userName, page, true); 167 | } else { 168 | return UserDao.getMemberDao(userName, page) 169 | } 170 | }; 171 | 172 | /** 173 | * 获取用户组织 174 | */ 175 | const getUserOrgs = async (page = 1, userName) => { 176 | if (page <= 1) { 177 | return UserDao.getUserOrgsDao(userName, page, true); 178 | } else { 179 | return UserDao.getUserOrgsDao(userName, page) 180 | } 181 | }; 182 | 183 | export default { 184 | initUserInfo, 185 | getUserInfo, 186 | getPersonUserInfo, 187 | clearUserInfo, 188 | getFollowerList, 189 | getFollowedList, 190 | getNotifation, 191 | setNotificationAsRead, 192 | setAllNotificationAsRead, 193 | updateUser, 194 | doFollow, 195 | checkFollow, 196 | getMember, 197 | getUserOrgs 198 | } 199 | -------------------------------------------------------------------------------- /app/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | import {applyMiddleware, createStore} from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | import reducers from './reducers'; 7 | 8 | export function createReducer(initialState, handlers) { 9 | return function reducer(state = initialState, action) { 10 | if (handlers.hasOwnProperty(action.type)) { 11 | return handlers[action.type](state, action); 12 | } else { 13 | return state; 14 | } 15 | } 16 | } 17 | 18 | const createStoreWithMW = applyMiddleware(thunk)(createStore); 19 | const store = createStoreWithMW(reducers); 20 | export default store -------------------------------------------------------------------------------- /app/store/reducers/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/16. 3 | */ 4 | 5 | import {EVENT} from '../type'; 6 | import {createReducer} from '../' 7 | 8 | const initialState = { 9 | //当前用户事件列表 10 | received_events_data_list: [], 11 | received_events_current_size: 0, 12 | }; 13 | 14 | const actionHandler = { 15 | [EVENT.RECEIVED_EVENTS]: (state, action) => { 16 | return { 17 | ...state, 18 | received_events_data_list: action.res, 19 | received_events_current_size: action.res.length 20 | } 21 | }, 22 | }; 23 | 24 | export default createReducer(initialState, actionHandler) 25 | -------------------------------------------------------------------------------- /app/store/reducers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | 5 | import {combineReducers} from 'redux'; 6 | import login from "./login" 7 | import user from "./user" 8 | import event from "./event" 9 | import repository from "./repository" 10 | import issue from "./issue" 11 | 12 | 13 | export default combineReducers({ 14 | login: login, 15 | user: user, 16 | event: event, 17 | repository: repository, 18 | issue: issue, 19 | }); 20 | 21 | export function clear(state) { 22 | state().event.received_events_data_list = []; 23 | state().repository.trend_repos_data_list = []; 24 | } -------------------------------------------------------------------------------- /app/store/reducers/issue.js: -------------------------------------------------------------------------------- 1 | import {ISSUSE} from '../type'; 2 | import {createReducer} from '../' 3 | 4 | const initialState = { 5 | 6 | }; 7 | 8 | const actionHandler = { 9 | 10 | }; 11 | 12 | export default createReducer(initialState, actionHandler) -------------------------------------------------------------------------------- /app/store/reducers/login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | import {LOGIN} from '../type'; 5 | import {createReducer} from '../' 6 | 7 | const initialState = { 8 | type: LOGIN.CLEAR, 9 | }; 10 | 11 | const actionHandler = { 12 | [LOGIN.IN]: (state, action) => { 13 | return {} 14 | }, 15 | [LOGIN.CLEAR]: (state, action) => { 16 | return {} 17 | } 18 | }; 19 | 20 | export default createReducer(initialState, actionHandler) -------------------------------------------------------------------------------- /app/store/reducers/repository.js: -------------------------------------------------------------------------------- 1 | import {REPOSITORY} from '../type'; 2 | import {createReducer} from '../' 3 | 4 | const initialState = { 5 | //当前趋势列表 6 | trend_repos_data_list: [], 7 | trend_repos_current_size: 0, 8 | }; 9 | 10 | const actionHandler = { 11 | [REPOSITORY.TREND_REPOSITORY]: (state, action) => { 12 | return { 13 | ...state, 14 | trend_repos_data_list: action.res, 15 | trend_repos_current_size: action.res.length 16 | } 17 | }, 18 | }; 19 | 20 | export default createReducer(initialState, actionHandler) -------------------------------------------------------------------------------- /app/store/reducers/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/16. 3 | */ 4 | import {USER} from '../type'; 5 | import {createReducer} from '../' 6 | 7 | const initialState = { 8 | //当前登录用户信息 9 | userInfo: {}, 10 | }; 11 | 12 | const actionHandler = { 13 | [USER.USER_INFO]: (state, action) => { 14 | return { 15 | ...state, 16 | userInfo: action.res 17 | } 18 | }, 19 | }; 20 | 21 | export default createReducer(initialState, actionHandler) 22 | -------------------------------------------------------------------------------- /app/store/type.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | 5 | 6 | 7 | export const LOGIN = { 8 | IN: 'LOGIN.IN', 9 | CLEAR: 'LOGIN.CLEAR', 10 | }; 11 | 12 | export const USER = { 13 | USER_INFO: 'user_info', 14 | }; 15 | 16 | 17 | export const EVENT = { 18 | RECEIVED_EVENTS: 'received_events', 19 | }; 20 | 21 | 22 | export const REPOSITORY = { 23 | TREND_REPOSITORY: 'trend_repository', 24 | }; 25 | 26 | export const ISSUSE = { 27 | }; -------------------------------------------------------------------------------- /app/style/constant.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/7. 3 | */ 4 | 5 | /****************颜色****************/ 6 | 7 | export const miWhite = '#ececec'; 8 | export const white = '#FFF'; 9 | export const transparentColor = '#00000000'; 10 | 11 | export const mainBackgroundColor = miWhite; 12 | export const tabBackgroundColor = '#ffffff'; 13 | export const cardBackgroundColor = '#FFF'; 14 | export const cardShadowColor = '#000000'; 15 | export const actionBlue = '#267aff'; 16 | 17 | export const lineColor = '#42464b'; 18 | 19 | export const primaryColor = '#24292e'; 20 | export const primaryDarkColor = '#121917'; 21 | export const primaryLightColor = '#42464b'; 22 | 23 | export const webDraculaBackgroundColor = '#282a36'; 24 | 25 | 26 | export const selectedColor = primaryDarkColor; 27 | 28 | export const titleTextColor = miWhite; 29 | export const mainTextColor = primaryDarkColor; 30 | export const subTextColor = '#959595'; 31 | export const subLightTextColor = '#c4c4c4'; 32 | export const TextColorWhite = '#FFFFFF'; 33 | export const TextColorMiWhtte = miWhite; 34 | 35 | export const tabSelectedColor = primaryColor; 36 | export const tabUnSelectColor = '#a6aaaf'; 37 | 38 | 39 | /****************大小****************/ 40 | // navbar 高度 41 | export const iosnavHeaderHeight = 70; 42 | export const andrnavHeaderHeight = 70; 43 | 44 | export const largetTextSize = 30; 45 | export const bigTextSize = 23; 46 | export const normalTextSize = 18; 47 | export const middleTextWhite = 16; 48 | export const smallTextSize = 14; 49 | export const minTextSize = 12; 50 | 51 | 52 | // tabBar 高度 53 | export const tabBarHeight = 44; 54 | export const tabIconSize = 20; 55 | 56 | 57 | export const normalIconSize = 40; 58 | export const bigIconSize = 50; 59 | export const largeIconSize = 80; 60 | export const smallIconSize = 30; 61 | export const minIconSize = 20; 62 | export const littleIconSize = 10; 63 | 64 | 65 | export const normalMarginEdge = 10; 66 | export const normalNumberOfLine = 4; 67 | 68 | /****************图标****************/ 69 | export const nextIcon = 'chevron-right'; 70 | export const tabRecommended = 'activity'; 71 | export const tabDynamic = 'aperture'; 72 | export const tabMy = 'users'; 73 | 74 | 75 | /****************常量****************/ 76 | export const TOKEN_KEY = "token"; 77 | export const USER_NAME_KEY = "user-name"; 78 | export const PW_KEY = "user-pw"; 79 | export const USER_BASIC_CODE = "user-basic-code"; 80 | export const USER_INFO = "user-info"; 81 | export const LANGUAGE_SELECT = "language-select"; 82 | export const LANGUAGE_SELECT_NAME = "language-select-name"; 83 | export const REFRESH_LANGUAGE = "refreshLanguageApp"; -------------------------------------------------------------------------------- /app/utils/backUtils.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { 3 | BackHandler 4 | } from 'react-native'; 5 | 6 | import I18n from '../style/i18n' 7 | import Toast from '../components/common/ToastProxy' 8 | 9 | import {Router, Actions, Scene} from 'react-native-router-flux'; 10 | 11 | export default function BackUtils() { 12 | let hasTip = false; 13 | let ts; 14 | return function () { 15 | if (Actions.state.routes[0].index > 0) { 16 | Actions.pop(); 17 | return true; 18 | } 19 | ts = Date.now(); 20 | if (!hasTip) { 21 | let handler = function () { 22 | let now = Date.now(); 23 | if (now - ts < 1000) { 24 | requestAnimationFrame(handler) 25 | } else { 26 | hasTip = false 27 | } 28 | }; 29 | handler(); 30 | hasTip = true; 31 | Toast(I18n("doublePressExit")); 32 | return true 33 | } else { 34 | BackHandler.exitApp(); 35 | //SplashScreen.exit(); 36 | return true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/utils/issueUtils.js: -------------------------------------------------------------------------------- 1 | import store from '../store' 2 | 3 | const {dispatch, getState} = store; 4 | 5 | 6 | export const isCommentOwner = (repositoryName, commentName) => { 7 | let login = getState()['user'].userInfo.login; 8 | return repositoryName === login || commentName === login 9 | }; 10 | 11 | export const isRepositoryOwner = (repositoryName) => { 12 | let login = getState()['user'].userInfo.login; 13 | return repositoryName === login 14 | }; -------------------------------------------------------------------------------- /app/utils/pulse/PulseUtils.js: -------------------------------------------------------------------------------- 1 | import {hostWeb} from '../../net/address' 2 | import * as Code from '../../net/netwrokCode' 3 | import I18n from '../../style/i18n' 4 | 5 | const mergedPullRequestsStart = ' { 24 | const timeoutId = setTimeout(() => { 25 | resolve({ 26 | result: false, 27 | status: Code.NETWORK_TIMEOUT, 28 | message: I18n('netTimeout') 29 | }) 30 | }, 15000); 31 | let url = `${hostWeb}${owner}/${repositoryName}/pulse`; 32 | if (__DEV__) { 33 | console.log("fetch url ", url) 34 | } 35 | fetch(url) 36 | .then((response) => { 37 | clearTimeout(timeoutId); 38 | return response.text() 39 | }) 40 | .catch((error) => { 41 | clearTimeout(timeoutId); 42 | reject({result: false, data: error}); 43 | console.log(error); 44 | }).then((responseData) => { 45 | let data = htmlToRepo(responseData); 46 | try { 47 | resolve({result: true, data: data}); 48 | } catch (e) { 49 | console.log(e); 50 | reject({result: false, data: e}); 51 | } 52 | }).done(); 53 | }); 54 | } 55 | 56 | 57 | const htmlToRepo = (responseData) => { 58 | let resultData = {}; 59 | let start = responseData.indexOf('
  • ', start); 61 | let data = responseData.substring(start, end) 62 | .replace(/[\n]/g, ''); 63 | 64 | start = data.indexOf(mergedPullRequestsStart); 65 | let middle = data.indexOf(middleTag, start); 66 | end = data.indexOf(endTag, start); 67 | resultData.mergedPull = data.substring(middle + middleLength, end).trim(); 68 | 69 | start = data.indexOf(proposedPullRequestsStart); 70 | middle = data.indexOf(middleTag, start); 71 | end = data.indexOf(endTag, start); 72 | resultData.proposedPull = data.substring(middle + middleLength, end).trim(); 73 | 74 | start = data.indexOf(closedIssue); 75 | middle = data.indexOf(middleTag, start); 76 | end = data.indexOf(endTag, start); 77 | resultData.closedIssue = data.substring(middle + middleLength, end).trim(); 78 | 79 | 80 | start = data.indexOf(openIssue); 81 | middle = data.indexOf(middleTag, start); 82 | end = data.indexOf(endTag, start); 83 | resultData.openIssue = data.substring(middle + middleLength, end).trim(); 84 | resultData.des = data.substring(middle + middleLength, end).trim(); 85 | 86 | start = responseData.indexOf(desStart); 87 | end = responseData.indexOf(desEnd, start); 88 | data = responseData.substring(start, end).trim(); 89 | resultData.des = data; 90 | 91 | start = responseData.indexOf(issueOpenStatusStart); 92 | end = responseData.indexOf(issueStatusEnd, start); 93 | if (start >= 0 && end >= 0) { 94 | data = responseData.substring(start + issueOpenStatusStart.length, end).trim(); 95 | if (data && data.indexOf("") >= 0) 96 | resultData.issueOpenStatus = data; 97 | } 98 | 99 | start = responseData.indexOf(issueClosedStatusStart); 100 | end = responseData.indexOf(issueStatusEnd, start); 101 | if (start >= 0 && end >= 0) { 102 | data = responseData.substring(start + issueClosedStatusStart.length, end).trim(); 103 | if (data && data.indexOf("") >= 0) 104 | resultData.issueClosedStatus = data; 105 | } 106 | 107 | return resultData; 108 | }; -------------------------------------------------------------------------------- /app/utils/timeUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by guoshuyu on 2017/11/11. 3 | */ 4 | 5 | import moment from 'moment'; 6 | import I18n from '../style/i18n' 7 | import momentLocale from 'moment/locale/zh-cn'; 8 | 9 | moment.updateLocale('zh-cn', momentLocale); 10 | 11 | const second = 1000; 12 | const min = second * 60; 13 | const hour = min * 60; 14 | const day = hour * 24; 15 | const week = day * 7; 16 | const year = day * 30 * 11; 17 | 18 | /** 19 | * 时间转化 20 | */ 21 | export default function resolveTime(longTime) { 22 | if (!longTime) 23 | return ""; 24 | let now = moment().toDate().getTime(); 25 | let time = moment(longTime).toDate().getTime(); 26 | let delta = now - time; 27 | return calcTimer(delta, time) 28 | } 29 | 30 | 31 | function calcTimer(delta, ori) { 32 | let res = ''; 33 | let list = Object.keys(calculator).sort((a, b) => b - a); 34 | for (let i = 0, key = list[i]; key; key = list[++i]) { 35 | if (delta > key) { 36 | let a = Math.floor(delta / key); 37 | let b = delta % key; 38 | res = calculator[key](a, b, ori); 39 | break 40 | } 41 | } 42 | if (res === '') { 43 | res = I18n('justNow') 44 | } 45 | return res 46 | } 47 | 48 | const calculator = { 49 | [year]:(a, b, ori) => { 50 | return moment(ori).format('YYYY-MM-DD') 51 | }, 52 | [week]: (a, b, ori) => { 53 | return moment(ori).format('MM-DD HH:mm') 54 | }, 55 | [day]: (a, b, ori) => { 56 | return a + I18n('daysAgo') 57 | }, 58 | [hour]: (a, b) => { 59 | return a + I18n('hoursAgo') 60 | }, 61 | [min]: (a, b) => { 62 | return a + I18n('minutesAgo') 63 | }, 64 | [second]: (a, b) => { 65 | return I18n('justNow') 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /app/utils/trending/GitHubTrending.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 从https://github.com/trending获取数据 3 | * 项目地址:https://github.com/crazycodeboy/GitHubTrending 4 | * 博客地址:http://www.devio.org 5 | * @flow 6 | */ 7 | import TrendingUtil from './TrendingUtil'; 8 | import * as Code from '../../net/netwrokCode' 9 | import I18n from '../../style/i18n' 10 | 11 | 12 | 13 | class GitHubTrending { 14 | 15 | fetchTrending(url) { 16 | return new Promise((resolve, reject) => { 17 | const timeoutId = setTimeout(() => { 18 | resolve({ 19 | result: false, 20 | status: Code.NETWORK_TIMEOUT, 21 | message: I18n('netTimeout') 22 | }) 23 | }, 15000); 24 | fetch(url) 25 | .then((response) => { 26 | clearTimeout(timeoutId); 27 | return response.text() 28 | }) 29 | .catch((error) => { 30 | clearTimeout(timeoutId); 31 | reject({result: false, data: error}); 32 | console.log(error); 33 | }).then((responseData) => { 34 | try { 35 | resolve({result: true, data: TrendingUtil.htmlToRepo(responseData)}); 36 | } catch (e) { 37 | reject({result: false, data: e}); 38 | } 39 | }).done(); 40 | }); 41 | } 42 | 43 | } 44 | 45 | export default new GitHubTrending(); 46 | -------------------------------------------------------------------------------- /app/utils/trending/StringUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 字符串工具类 3 | * 项目地址:https://github.com/crazycodeboy/GitHubTrending 4 | * 博客地址:http://www.devio.org 5 | * @flow 6 | */ 7 | export default class StringUtil { 8 | /* 9 | * 去掉字符串左右空格、换行 10 | */ 11 | static trim(text) { 12 | if (typeof(text) == "string") { 13 | return text.replace(/^\s*|\s*$/g, ""); 14 | } 15 | else { 16 | return text; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/utils/trending/TrendingRepoModel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TrendingRepoModel 3 | * 项目地址:https://github.com/crazycodeboy/GitHubTrending 4 | * 博客地址:http://www.devio.org 5 | * @flow 6 | */ 7 | 8 | export default class TrendingRepoModel { 9 | constructor(fullName, url, description, language, meta, contributors, contributorsUrl, 10 | starCount, forkCount, name, reposName) { 11 | this.fullName = fullName; 12 | this.url = url; 13 | this.description = description; 14 | this.language = language; 15 | this.meta = meta; 16 | this.contributors = contributors; 17 | this.contributorsUrl = contributorsUrl; 18 | this.starCount = starCount; 19 | this.forkCount = forkCount; 20 | this.name = name; 21 | this.reposName = reposName; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/utils/trending/TrendingUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TrendingUtil 3 | * 工具类:用于将github trending html 转换成 TrendingRepoModel 4 | * 项目地址:https://github.com/crazycodeboy/GitHubTrending 5 | * 博客地址:http://www.devio.org 6 | * @flow 7 | */ 8 | 9 | 10 | import TrendingRepoModel from './TrendingRepoModel'; 11 | import StringUtil from './StringUtil'; 12 | 13 | var TAGS = { 14 | meta: { 15 | start: '', 17 | end: 'end' 18 | }, 19 | starCount: { 20 | start: '', 22 | end: '' 23 | }, 24 | forkCount: { 25 | start: '', 27 | end: '' 28 | } 29 | 30 | } 31 | export default class TrendingUtil { 32 | static htmlToRepo(responseData) { 33 | responseData = responseData.replace(/\n/, ''); 34 | var repos = []; 35 | var splitWithH3 = responseData.split('', '') + "end"; 44 | repo.meta = this.parseRepoLabelWithTag(repo, metaNoteContent, TAGS.meta); 45 | repo.starCount = this.parseRepoLabelWithTag(repo, metaNoteContent, TAGS.starCount); 46 | repo.forkCount = this.parseRepoLabelWithTag(repo, metaNoteContent, TAGS.forkCount); 47 | 48 | this.parseRepoLang(repo, metaNoteContent); 49 | this.parseRepoContributors(repo, metaNoteContent); 50 | repos.push(repo); 51 | } 52 | return repos; 53 | } 54 | 55 | static parseContentWithNote(htmlStr, startFlag, endFlag) { 56 | var noteStar = htmlStr.indexOf(startFlag); 57 | if (noteStar == -1) { 58 | return ''; 59 | } else { 60 | noteStar += +startFlag.length; 61 | } 62 | 63 | var noteEnd = htmlStr.indexOf(endFlag, noteStar); 64 | var content = htmlStr.substring(noteStar, noteEnd); 65 | return StringUtil.trim(content) 66 | } 67 | 68 | static parseRepoBaseInfo(repo, htmlBaseInfo) { 69 | var urlIndex = htmlBaseInfo.indexOf('', urlIndex)); 71 | repo.url = url; 72 | repo.fullName = url.slice(1, url.length); 73 | if (repo.fullName && repo.fullName.indexOf('/') !== -1) { 74 | repo.name = repo.fullName.split('/')[0]; 75 | repo.reposName = repo.fullName.split('/')[1]; 76 | } 77 | 78 | var description = this.parseContentWithNote(htmlBaseInfo, '

    ', '

    '); 79 | repo.description = description; 80 | } 81 | 82 | static parseRepoLabelWithTag(repo, noteContent, tag) { 83 | let startFlag; 84 | if (TAGS.starCount === tag || TAGS.forkCount === tag) { 85 | //startFlag = tag.start + ' href="/' + repo.fullName + tag.flag; 86 | startFlag = tag.start; 87 | } else { 88 | startFlag = tag.start; 89 | } 90 | let content = this.parseContentWithNote(noteContent, startFlag, tag.end); 91 | let metaContent = content.substring(content.indexOf(tag["flag"]) + tag["flag"].length, content.length); 92 | return StringUtil.trim(metaContent); 93 | } 94 | 95 | static parseRepoLang(repo, metaNoteContent) { 96 | var content = this.parseContentWithNote(metaNoteContent, 'programmingLanguage">', ''); 97 | repo.language = StringUtil.trim(content); 98 | } 99 | 100 | static parseRepoContributors(repo, htmlContributors) { 101 | htmlContributors = this.parseContentWithNote(htmlContributors, 'Built by', ''); 102 | var splitWitSemicolon = htmlContributors.split('"'); 103 | repo.contributorsUrl = splitWitSemicolon[1]; 104 | var contributors = []; 105 | for (var i = 0; i < splitWitSemicolon.length; i++) { 106 | var url = splitWitSemicolon[i]; 107 | if (url.search('http') !== -1) { 108 | contributors.push(url); 109 | } 110 | } 111 | repo.contributors = contributors; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: [ 4 | ['@babel/plugin-proposal-decorators', { 'legacy': true }], 5 | ['import', { libraryName: '@ant-design/react-native' }] // 与 Web 平台的区别是不需要设置 style 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/download.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /ios/GSYGithubApp-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /ios/GSYGithubApp-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /ios/GSYGithubApp-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/GSYGithubApp.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/GSYGithubApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 19 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 20 | moduleName:@"GSYGithubApp" 21 | initialProperties:nil]; 22 | 23 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 24 | 25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 26 | UIViewController *rootViewController = [UIViewController new]; 27 | rootViewController.view = rootView; 28 | self.window.rootViewController = rootViewController; 29 | [self.window makeKeyAndVisible]; 30 | return YES; 31 | } 32 | 33 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 34 | { 35 | #if DEBUG 36 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 37 | #else 38 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 39 | #endif 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "logo-2.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "logo2x-2.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "logo-3.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "logo2x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "logo2x-3.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "logo-4.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "idiom" : "iphone", 41 | "size" : "60x60", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "idiom" : "iphone", 46 | "size" : "60x60", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ios-marketing", 51 | "size" : "1024x1024", 52 | "scale" : "1x" 53 | } 54 | ], 55 | "info" : { 56 | "version" : 1, 57 | "author" : "xcode" 58 | } 59 | } -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-2.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-3.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo-4.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x-2.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x-3.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/AppIcon.appiconset/logo2x.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "2436h", 7 | "filename" : "Default@3x-3.png", 8 | "minimum-system-version" : "11.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "2436h", 16 | "filename" : "Default@3x-4.png", 17 | "minimum-system-version" : "11.0", 18 | "orientation" : "landscape", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "736h", 25 | "filename" : "Default@3x.png", 26 | "minimum-system-version" : "8.0", 27 | "orientation" : "portrait", 28 | "scale" : "3x" 29 | }, 30 | { 31 | "orientation" : "landscape", 32 | "idiom" : "iphone", 33 | "extent" : "full-screen", 34 | "minimum-system-version" : "8.0", 35 | "subtype" : "736h", 36 | "scale" : "3x" 37 | }, 38 | { 39 | "extent" : "full-screen", 40 | "idiom" : "iphone", 41 | "subtype" : "667h", 42 | "filename" : "Default@2x.png", 43 | "minimum-system-version" : "8.0", 44 | "orientation" : "portrait", 45 | "scale" : "2x" 46 | }, 47 | { 48 | "orientation" : "portrait", 49 | "idiom" : "iphone", 50 | "extent" : "full-screen", 51 | "minimum-system-version" : "7.0", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "orientation" : "portrait", 56 | "idiom" : "iphone", 57 | "extent" : "full-screen", 58 | "minimum-system-version" : "7.0", 59 | "subtype" : "retina4", 60 | "scale" : "2x" 61 | }, 62 | { 63 | "orientation" : "portrait", 64 | "idiom" : "iphone", 65 | "extent" : "full-screen", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "orientation" : "portrait", 70 | "idiom" : "iphone", 71 | "extent" : "full-screen", 72 | "scale" : "2x" 73 | }, 74 | { 75 | "orientation" : "portrait", 76 | "idiom" : "iphone", 77 | "extent" : "full-screen", 78 | "subtype" : "retina4", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | } 86 | } -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@2x.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-1.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-2.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-3.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x-4.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage-1.launchimage/Default@3x.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "2436h", 7 | "filename" : "Default@3x-3.png", 8 | "minimum-system-version" : "11.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "736h", 16 | "filename" : "Default@3x.png", 17 | "minimum-system-version" : "8.0", 18 | "orientation" : "portrait", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "736h", 25 | "filename" : "Default@3x-1.png", 26 | "minimum-system-version" : "8.0", 27 | "orientation" : "landscape", 28 | "scale" : "3x" 29 | }, 30 | { 31 | "extent" : "full-screen", 32 | "idiom" : "iphone", 33 | "subtype" : "667h", 34 | "filename" : "Default@2x.png", 35 | "minimum-system-version" : "8.0", 36 | "orientation" : "portrait", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "extent" : "full-screen", 41 | "idiom" : "iphone", 42 | "subtype" : "retina4", 43 | "filename" : "Default_640x1136.png", 44 | "minimum-system-version" : "7.0", 45 | "orientation" : "portrait", 46 | "unassigned" : true, 47 | "scale" : "2x" 48 | }, 49 | { 50 | "orientation" : "portrait", 51 | "idiom" : "iphone", 52 | "filename" : "Default@2x-1.png", 53 | "extent" : "full-screen", 54 | "unassigned" : true, 55 | "scale" : "1x" 56 | }, 57 | { 58 | "orientation" : "portrait", 59 | "idiom" : "iphone", 60 | "filename" : "Default@3x-2.png", 61 | "extent" : "full-screen", 62 | "unassigned" : true, 63 | "scale" : "2x" 64 | }, 65 | { 66 | "extent" : "full-screen", 67 | "idiom" : "iphone", 68 | "subtype" : "retina4", 69 | "filename" : "Default_640x1136-1.png", 70 | "orientation" : "portrait", 71 | "unassigned" : true, 72 | "scale" : "2x" 73 | } 74 | ], 75 | "info" : { 76 | "version" : 1, 77 | "author" : "xcode" 78 | } 79 | } -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@2x-1.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-1.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-2.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x-3.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default@3x.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default_640x1136-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default_640x1136-1.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default_640x1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios/GSYGithubApp/Images.xcassets/LaunchImage.launchimage/Default_640x1136.png -------------------------------------------------------------------------------- /ios/GSYGithubApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | GSYGithubAPP 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 | 3.3 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 18 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSCameraUsageDescription 41 | 是否允许应用访问您的相机 42 | NSLocationWhenInUseUsageDescription 43 | 是否允许应用访问您的位置 44 | NSMicrophoneUsageDescription 45 | 是否允许应用访问您的麦克风 46 | NSPhotoLibraryUsageDescription 47 | 是否允许应用访问您的相册 48 | UIAppFonts 49 | 50 | AntDesign.ttf 51 | Entypo.ttf 52 | EvilIcons.ttf 53 | Feather.ttf 54 | FontAwesome.ttf 55 | FontAwesome5_Brands.ttf 56 | FontAwesome5_Regular.ttf 57 | FontAwesome5_Solid.ttf 58 | Foundation.ttf 59 | Ionicons.ttf 60 | MaterialIcons.ttf 61 | MaterialCommunityIcons.ttf 62 | SimpleLineIcons.ttf 63 | Octicons.ttf 64 | Zocial.ttf 65 | Fontisto.ttf 66 | 67 | UILaunchStoryboardName 68 | launch 69 | UIRequiredDeviceCapabilities 70 | 71 | armv7 72 | 73 | UISupportedInterfaceOrientations 74 | 75 | UIInterfaceOrientationPortrait 76 | UIInterfaceOrientationLandscapeLeft 77 | UIInterfaceOrientationLandscapeRight 78 | 79 | UIViewControllerBasedStatusBarAppearance 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /ios/GSYGithubApp/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios/GSYGithubAppTests/GSYGithubAppTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 16 | 17 | @interface GSYGithubAppTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation GSYGithubAppTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | #ifdef DEBUG 44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 45 | if (level >= RCTLogLevelError) { 46 | redboxError = message; 47 | } 48 | }); 49 | #endif 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | #ifdef DEBUG 64 | RCTSetLogFunction(RCTDefaultLogFunction); 65 | #endif 66 | 67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 69 | } 70 | 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /ios/GSYGithubAppTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | target 'GSYGithubApp' do 5 | # Pods for GSYGithubApp 6 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" 7 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" 8 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" 9 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" 10 | pod 'React', :path => '../node_modules/react-native/' 11 | pod 'React-Core', :path => '../node_modules/react-native/' 12 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' 13 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' 14 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' 15 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' 16 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' 17 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' 18 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' 19 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' 20 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' 21 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' 22 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' 23 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' 24 | 25 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' 26 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' 27 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' 28 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' 29 | pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon" 30 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" 31 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' 32 | 33 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' 34 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' 35 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' 36 | 37 | pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' 38 | 39 | target 'GSYGithubAppTests' do 40 | inherit! :search_paths 41 | # Pods for testing 42 | end 43 | 44 | use_native_modules! 45 | end 46 | 47 | target 'GSYGithubApp-tvOS' do 48 | # Pods for GSYGithubApp-tvOS 49 | 50 | target 'GSYGithubApp-tvOSTests' do 51 | inherit! :search_paths 52 | # Pods for testing 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /ios/tmp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // tmp.swift 3 | // GSYGithubApp 4 | // 5 | // Created by 郭树煜 on 2019/7/16. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /ios_wait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/ios_wait.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/logo.png -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GSYGithubApp", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "react-native start", 7 | "test": "jest", 8 | "lint": "eslint ." 9 | }, 10 | "dependencies": { 11 | "@react-native-community/async-storage": "^1.2.4", 12 | "@react-native-community/netinfo": "^2.0.0", 13 | "@react-native-community/viewpager": "^1.1.7", 14 | "base-64": "^0.1.0", 15 | "buffer": "^5.0.8", 16 | "crypto-js": "^3.1.9-1", 17 | "highlight.js": "^9.12.0", 18 | "lottie-ios": "3.0.3", 19 | "lottie-react-native": "^3.0.2", 20 | "marked": "^0.3.6", 21 | "moment": "^2.19.1", 22 | "react": "16.9.0", 23 | "react-native": "0.61.3", 24 | "react-native-autolink": "^1.4.0", 25 | "react-native-gesture-handler": "^1.1.0", 26 | "react-native-htmlview": "^0.14.0", 27 | "react-native-i18n": "^2.0.15", 28 | "react-native-image-crop-picker": "^0.25.0", 29 | "react-native-image-zoom-viewer": "^2.2.26", 30 | "react-native-reanimated": "^1.1.0", 31 | "react-native-root-toast": "^2.2.0", 32 | "react-native-router-flux": "^4.1.0-beta.5", 33 | "react-native-scrollable-tab-view-fix-guo": "^0.99.0", 34 | "react-native-spinkit-fix-new": "git+https://github.com/CarSmallGuo/react-native-spinkit.git", 35 | "react-native-tab-view": "^2.0.9", 36 | "react-native-textinput-effects": "^0.4.1", 37 | "react-native-vector-icons": "^6.6.0", 38 | "react-native-version-number-fix-new": "git+https://github.com/CarSmallGuo/react-native-version-number.git", 39 | "react-native-webview": "^5.6.2", 40 | "react-navigation": "^3.1.0", 41 | "react-redux": "^6.0.1", 42 | "realm": "^2.29.1", 43 | "redux": "^4.0.4", 44 | "redux-thunk": "^2.3.0", 45 | "showdown": "^1.8.2", 46 | "url-parse": "^1.2.0" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.5.4", 50 | "@babel/plugin-proposal-decorators": "7.4.0", 51 | "@babel/runtime": "^7.5.4", 52 | "@react-native-community/eslint-config": "^0.0.5", 53 | "babel-jest": "^24.8.0", 54 | "babel-plugin-import": "^1.11.0", 55 | "eslint": "^6.0.1", 56 | "jest": "^24.8.0", 57 | "metro-react-native-babel-preset": "^0.55.0", 58 | "react-test-renderer": "16.8.6" 59 | }, 60 | "jest": { 61 | "preset": "react-native" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /register0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/register0.jpg -------------------------------------------------------------------------------- /register1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/register1.jpg -------------------------------------------------------------------------------- /thanks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/GSYGithubAPP/29fd25b91c63de1d7be1eb1a3993693649087135/thanks.jpg --------------------------------------------------------------------------------