├── .watchmanconfig
├── ios
├── Podfile
├── Classes
│ ├── H5
│ │ ├── GFWebResourceURLProtocol.h
│ │ ├── GFWebResourceInterceptorSettings+Internal.h
│ │ ├── GFWebResourceInterceptorSettings.m
│ │ ├── GFWebResourceInterceptorSettings.h
│ │ ├── GFWebResourceCache.h
│ │ └── GFWebResourceInterceptor.h
│ ├── DXRNUtils.h
│ ├── DXRefreshControl.h
│ ├── GFDiskCacheManager.h
│ ├── DXTopMessageManager.h
│ ├── Additions
│ │ ├── NSString+GFAdditions.h
│ │ └── NSString+GFAdditions.m
│ ├── GFDiskCacheManager.m
│ ├── DXRNUtils.m
│ ├── NavigationCategory
│ │ └── DXTextCategoryMenu.h
│ ├── UIView+TopBarMessage.h
│ ├── DXTopMessageManager.m
│ └── DXFileManager.h
├── gitfeed
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-40@2x.png
│ │ │ ├── Icon-40@3x.png
│ │ │ ├── Icon-60@2x.png
│ │ │ ├── Icon-60@3x.png
│ │ │ ├── Icon-40@2x-1.png
│ │ │ ├── Icon-40@3x-1.png
│ │ │ ├── Icon-Small@2x.png
│ │ │ ├── Icon-Small@3x.png
│ │ │ └── Contents.json
│ ├── gitfeed.entitlements
│ ├── AppDelegate.h
│ ├── main.m
│ ├── AppDelegate.m
│ ├── Info.plist
│ └── Base.lproj
│ │ └── LaunchScreen.xib
├── Podfile.lock
├── gitfeed.xcworkspace
│ └── contents.xcworkspacedata
└── gitfeedTests
│ ├── Info.plist
│ └── gitfeedTests.m
├── .babelrc
├── AppIcons
├── ios
│ ├── Icon.png
│ ├── Icon-60.png
│ ├── Icon-72.png
│ ├── Icon@2x.png
│ ├── Icon-72@2x.png
│ ├── Icon-Small-50.png
│ ├── iTunesArtwork.png
│ ├── Icon-Small-50@2x.png
│ ├── iTunesArtwork@2x.png
│ ├── AppIcon.appiconset
│ │ ├── Icon-40.png
│ │ ├── Icon-76.png
│ │ ├── Icon-40@2x.png
│ │ ├── Icon-40@3x.png
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small@2x.png
│ │ ├── Icon-Small@3x.png
│ │ └── Contents.json
│ └── README.md
├── android
│ ├── playstore-icon.png
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ └── drawable-xxxhdpi
│ │ └── ic_launcher.png
└── watchkit
│ └── AppIcon.appiconset
│ ├── Icon-24@2x.png
│ ├── Icon-29@2x.png
│ ├── Icon-29@3x.png
│ ├── Icon-40@2x.png
│ ├── Icon-44@2x.png
│ ├── Icon-86@2x.png
│ ├── Icon-98@2x.png
│ ├── Icon-27.5@2x.png
│ └── Contents.json
├── js
├── assets
│ ├── chat@2x.png
│ ├── chat@3x.png
│ ├── ding@2x.png
│ ├── ding@3x.png
│ ├── pyq@2x.png
│ ├── pyq@3x.png
│ ├── push-nux.png
│ ├── push-nux@2x.png
│ ├── push-nux@3x.png
│ ├── colorful-windows.jpg
│ ├── ic_detail_love@2x.png
│ └── ic_detail_unlove@2x.png
├── common
│ ├── WLStyles.js
│ ├── BlurView
│ │ ├── index.js
│ │ ├── BlurView.ios.js
│ │ └── BlurView.android.js
│ ├── Button.ios.js
│ ├── markdown
│ │ └── issue-html.js
│ ├── Button.android.js
│ ├── CommonStyles.js
│ ├── Pagination.js
│ ├── Util.js
│ ├── MarkdownView.js
│ ├── RoundTagButton.js
│ ├── GridView.js
│ ├── ReadMore.js
│ ├── UserCell.js
│ ├── F8StyleSheet.js
│ ├── F8Touchable.js
│ ├── GridPage.js
│ ├── F8Colors.js
│ ├── IconCell.js
│ ├── TabBarAndroid.js
│ ├── SettingsCell.js
│ ├── Placeholder.js
│ ├── F8Text.js
│ ├── DefaultTabBar.js
│ ├── PushNUXModal.js
│ ├── RepoCell.js
│ └── F8Button.js
├── services
│ ├── BridgeUtils.js
│ ├── Share.js
│ ├── xFetch.js
│ ├── Login.js
│ ├── LookFetch.js
│ └── Storage.js
├── actions
│ ├── navigation.js
│ ├── language.js
│ ├── index.js
│ ├── installation.js
│ ├── search.js
│ ├── login.js
│ ├── parse.js
│ └── notifications.js
├── reducers
│ ├── navigation.js
│ ├── index.js
│ ├── search.js
│ ├── login.js
│ ├── language.js
│ └── notifications.js
├── constants
│ ├── Styles.js
│ ├── Alerts.js
│ ├── Layout.js
│ └── Colors.js
├── pages
│ ├── WebSharePage.js
│ ├── FavPage.js
│ ├── RecentPage.js
│ ├── FamousPage.js
│ ├── NotificationPage.js
│ ├── HomePage.js
│ ├── Languages.js
│ ├── TrendPicker.js
│ ├── ShowcasePage.js
│ ├── DetailPage.js
│ ├── HotTags.js
│ ├── SearchHistory.js
│ ├── IssueDetailPage.js
│ ├── NotificationCell.js
│ └── PersonPage.js
├── store
│ ├── array.js
│ ├── analytics.js
│ ├── configureStore.js
│ ├── track.js
│ └── promise.js
├── _Playgroun.js
├── navigation
│ ├── Router.js
│ └── RootNavigation.js
├── Playground.js
├── setup.js
├── env.js
├── WLApp.js
└── PushNotificationsController.js
├── .buckconfig
├── android
├── app
│ ├── src
│ │ └── main
│ │ │ ├── assets
│ │ │ └── fonts
│ │ │ │ ├── Entypo.ttf
│ │ │ │ ├── Ionicons.ttf
│ │ │ │ ├── Octicons.ttf
│ │ │ │ ├── Zocial.ttf
│ │ │ │ ├── EvilIcons.ttf
│ │ │ │ ├── Foundation.ttf
│ │ │ │ ├── FontAwesome.ttf
│ │ │ │ ├── MaterialIcons.ttf
│ │ │ │ └── SimpleLineIcons.ttf
│ │ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── gitfeed
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ │ └── AndroidManifest.xml
│ ├── BUCK
│ └── proguard-rules.pro
├── keystores
│ ├── debug.keystore.properties
│ └── BUCK
├── build.gradle
├── settings.gradle
├── gradle.properties
└── gradlew.bat
├── index.ios.js
├── index.android.js
├── __tests__
├── index.ios.js
└── index.android.js
├── package.json
├── README.md
├── .flowconfig
└── .gitignore
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | pod 'UMengAnalytics-NO-IDFA'
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-native-stage-0/decorator-support"]
3 | }
--------------------------------------------------------------------------------
/AppIcons/ios/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon.png
--------------------------------------------------------------------------------
/js/assets/chat@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/chat@2x.png
--------------------------------------------------------------------------------
/js/assets/chat@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/chat@3x.png
--------------------------------------------------------------------------------
/js/assets/ding@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/ding@2x.png
--------------------------------------------------------------------------------
/js/assets/ding@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/ding@3x.png
--------------------------------------------------------------------------------
/js/assets/pyq@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/pyq@2x.png
--------------------------------------------------------------------------------
/js/assets/pyq@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/pyq@3x.png
--------------------------------------------------------------------------------
/js/assets/push-nux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/push-nux.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon-60.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon-72.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon@2x.png
--------------------------------------------------------------------------------
/js/assets/push-nux@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/push-nux@2x.png
--------------------------------------------------------------------------------
/js/assets/push-nux@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/push-nux@3x.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon-72@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon-Small-50.png
--------------------------------------------------------------------------------
/AppIcons/ios/iTunesArtwork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/iTunesArtwork.png
--------------------------------------------------------------------------------
/js/assets/colorful-windows.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/colorful-windows.jpg
--------------------------------------------------------------------------------
/js/assets/ic_detail_love@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/ic_detail_love@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/iTunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/iTunesArtwork@2x.png
--------------------------------------------------------------------------------
/js/assets/ic_detail_unlove@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/js/assets/ic_detail_unlove@2x.png
--------------------------------------------------------------------------------
/AppIcons/android/playstore-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/playstore-icon.png
--------------------------------------------------------------------------------
/js/common/WLStyles.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | separator: {
3 | height: 1.0,
4 | backgroundColor: '#EEEEEE'
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceURLProtocol.h:
--------------------------------------------------------------------------------
1 | @import Foundation;
2 |
3 | @interface GFWebResourceURLProtocol : NSURLProtocol
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/AppIcons/android/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AppIcons/android/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/AppIcons/android/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/AppIcons/android/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AppIcons/android/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/AppIcons/android/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/android/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/ios/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-24@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-24@2x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-29@2x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-29@3x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-44@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-44@2x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-86@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-86@2x.png
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-98@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-98@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/keystores/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Icon-27.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/AppIcons/watchkit/AppIcon.appiconset/Icon-27.5@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/js/services/BridgeUtils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | import { NativeModules } from 'react-native'
6 | export default NativeModules.DXRNUtils
7 |
8 |
--------------------------------------------------------------------------------
/android/keystores/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = 'debug',
3 | store = 'debug.keystore',
4 | properties = 'debug.keystore.properties',
5 | visibility = [
6 | 'PUBLIC',
7 | ],
8 | )
9 |
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@3x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-40@3x-1.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiekw2010/react-native-gitfeed/HEAD/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/js/actions/navigation.js:
--------------------------------------------------------------------------------
1 | export const ROOT_SWITCH_TAB = 'ROOT_SWITCH_TAB'
2 |
3 | export const rootSwitchTab = (rootTab) => {
4 | return {
5 | type: ROOT_SWITCH_TAB,
6 | rootTab
7 | }
8 | }
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React from 'react'
4 | import {
5 | AppRegistry
6 | } from 'react-native'
7 | import setup from './js/setup'
8 |
9 | AppRegistry.registerComponent('gitfeed', setup)
10 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React from 'react'
4 | import {
5 | AppRegistry
6 | } from 'react-native'
7 | import setup from './js/setup'
8 |
9 | AppRegistry.registerComponent('gitfeed', setup)
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | gitfeed
7 |
8 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - UMengAnalytics-NO-IDFA (4.1.5)
3 |
4 | DEPENDENCIES:
5 | - UMengAnalytics-NO-IDFA
6 |
7 | SPEC CHECKSUMS:
8 | UMengAnalytics-NO-IDFA: f1b08b540eba6ebad044a574cae6d988cf40cfda
9 |
10 | COCOAPODS: 0.39.0
11 |
--------------------------------------------------------------------------------
/js/common/BlurView/index.js:
--------------------------------------------------------------------------------
1 | import * as BView from './BlurView'
2 |
3 | module.exports = BView.default
4 |
5 | if (global) {
6 | global.__exponent = {
7 | Components: {
8 | BlurView: BView.default
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceInterceptorSettings+Internal.h:
--------------------------------------------------------------------------------
1 | #import "GFWebResourceInterceptorSettings.h"
2 |
3 | /**
4 | * Web资源拦截器规则配置类的内部方法。
5 | */
6 | @interface GFWebResourceInterceptorSettings (Internal)
7 |
8 | // 创建默认拦截设置
9 | + (instancetype)defaultInterceptorSettings;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/ios/gitfeed/gitfeed.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/gitfeed.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Classes/DXRNUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXRNUtils.h
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/11/18.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RCTBridgeModule.h"
11 |
12 | @interface DXRNUtils : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/__tests__/index.ios.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.ios.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/ios/Classes/DXRefreshControl.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXRefreshControl.h
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/10/20.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RCTBridgeModule.h"
11 |
12 | @interface DXRefreshControl : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/ios/Classes/GFDiskCacheManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // GFDiskCacheManager.h
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 16/1/19.
6 | // Copyright © 2016年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RCTBridgeModule.h"
11 |
12 | @interface GFDiskCacheManager : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/js/common/Button.ios.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactNative = require('react-native');
3 | const {
4 | TouchableOpacity,
5 | View,
6 | } = ReactNative;
7 |
8 | const Button = (props) => {
9 | return
10 | {props.children}
11 | ;
12 | };
13 |
14 | module.exports = Button;
15 |
--------------------------------------------------------------------------------
/__tests__/index.android.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.android.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/ios/Classes/DXTopMessageManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXTopMessageManager.h
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/10/21.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RCTBridgeModule.h"
11 |
12 | @interface DXTopMessageManager : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/js/actions/language.js:
--------------------------------------------------------------------------------
1 | export const CHANGE_LANGUAGE = 'CHANGE_LANGUAGE'
2 | export const CHANGE_RULE = 'CHANGE_RULE'
3 |
4 | export const changeLanguage = language => {
5 | return {
6 | type: CHANGE_LANGUAGE,
7 | currentLanguage: language
8 | }
9 | }
10 |
11 | export const changeRule = rule => {
12 | return {
13 | type: CHANGE_RULE,
14 | currentRule: rule
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/js/reducers/navigation.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import * as nv from '../actions/navigation'
4 |
5 | const initialState = {
6 | rootTab: {
7 | id: 'HomePage',
8 | }
9 | }
10 |
11 | export const navigation = (state = initialState, action) => {
12 | switch (action.type) {
13 | case nv.ROOT_SWITCH_TAB:
14 | return { ...state, rootTab: action.rootTab }
15 | }
16 |
17 | return state
18 | }
19 |
--------------------------------------------------------------------------------
/js/constants/Styles.js:
--------------------------------------------------------------------------------
1 | import {
2 | Dimensions,
3 | StyleSheet
4 | } from 'react-native'
5 |
6 | import Colors from './Colors'
7 |
8 | export default {
9 | separatorBorderStyle: {
10 | borderBottomWidth: StyleSheet.hairlineWidth,
11 | borderBottomColor: Colors.cellBorder,
12 | },
13 | separatorViewStyle: {
14 | height: StyleSheet.hairlineWidth,
15 | backgroundColor: Colors.cellBorder,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/js/common/markdown/issue-html.js:
--------------------------------------------------------------------------------
1 | import css from './issue_css'
2 |
3 | export default `
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 | $body
16 |
17 | `
18 |
19 |
--------------------------------------------------------------------------------
/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { NavigationReducer } from '@exponent/ex-navigation'
2 | import { combineReducers } from 'redux'
3 |
4 | const notifications = require('./notifications')
5 | const login = require('./login')
6 | const search = require('./search')
7 | const language = require('./language')
8 |
9 | export default combineReducers({
10 | navigation: NavigationReducer,
11 | ...notifications,
12 | ...login,
13 | ...search,
14 | ...language
15 | })
--------------------------------------------------------------------------------
/ios/Classes/Additions/NSString+GFAdditions.h:
--------------------------------------------------------------------------------
1 | @import Foundation;
2 |
3 | /**
4 | * 提供`NSString`的通用扩展方法
5 | */
6 | @interface NSString (GFAdditions)
7 |
8 | /**
9 | * ---------------------------------------------------------------------------
10 | * @name 消息摘要
11 | * ---------------------------------------------------------------------------
12 | */
13 |
14 | /**
15 | * 生成MD5信息摘要
16 | *
17 | * @return 返回MD5信息摘要
18 | */
19 | - (NSString *)jm_MD5Digest;
20 |
21 | @end
--------------------------------------------------------------------------------
/js/constants/Alerts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @providesModule Alerts
3 | * @flow
4 | */
5 |
6 | import { StyleSheet } from 'react-native';
7 |
8 | export default {
9 | error: StyleSheet.create({
10 | container: {
11 | backgroundColor: 'red',
12 | },
13 | text: {
14 | color: 'white',
15 | },
16 | }),
17 | warning: StyleSheet.create({
18 | container: {
19 | backgroundColor: '#EAEB5E',
20 | },
21 | text: {
22 | color: '#666804',
23 | },
24 | }),
25 | }
26 |
--------------------------------------------------------------------------------
/js/common/Button.android.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactNative = require('react-native');
3 | const {
4 | TouchableNativeFeedback,
5 | View,
6 | } = ReactNative;
7 |
8 | const Button = (props) => {
9 | return
14 | {props.children}
15 | ;
16 | };
17 |
18 | module.exports = Button;
19 |
--------------------------------------------------------------------------------
/ios/gitfeed/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (nonatomic, strong) UIWindow *window;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/js/services/Share.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | import { NativeModules } from 'react-native'
6 | const ShareCenter = NativeModules.MEShareCenter
7 |
8 | /**
9 | *
10 | * @param uri Image URI
11 | * @param scene
12 | */
13 | //enum WXScene {
14 | // WXSceneSession = 0, /**< 聊天界面 */
15 | // WXSceneTimeline = 1, /**< 朋友圈 */
16 | // WXSceneFavorite = 2, /**< 收藏 */
17 | //};
18 | exports.shareWXImage = ShareCenter.shareWXImage
19 | exports.shareWebLink = ShareCenter.shareWebLink
20 |
--------------------------------------------------------------------------------
/ios/gitfeed/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "AppDelegate.h"
13 |
14 | int main(int argc, char * argv[]) {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/js/actions/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // Note: 这里只能用 CMD 的方式来加载每个 actions 里的对象, 原因参见 http://es6.ruanyifeng.com/#docs/module
4 |
5 | const login = require('./login')
6 | const notifications = require('./notifications')
7 | const installation = require('./installation')
8 | const parse = require('./parse')
9 | const navigation = require('./navigation')
10 | const search = require('./search')
11 | const language = require('./language')
12 |
13 | module.exports = {
14 | ...login,
15 | ...notifications,
16 | ...installation,
17 | ...parse,
18 | ...navigation,
19 | ...search,
20 | ...language
21 | }
22 |
--------------------------------------------------------------------------------
/js/common/CommonStyles.js:
--------------------------------------------------------------------------------
1 | const React = require('react-native');
2 | const Colors = require('./Colors');
3 |
4 | const {
5 | StyleSheet,
6 | } = React;
7 |
8 | const commonStyles = StyleSheet.create({
9 | container: {
10 | flex: 1,
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | },
14 |
15 | shadowLine: {
16 | shadowColor: '#999999',
17 | shadowOpacity: 0.8,
18 | shadowRadius: 1,
19 | shadowOffset: {
20 | height: 2,
21 | width: 1
22 | },
23 | },
24 |
25 | sepLine: {
26 | backgroundColor: Colors.backGray,
27 | height: 0.5,
28 | },
29 | });
30 |
31 | module.exports = commonStyles;
32 |
--------------------------------------------------------------------------------
/js/pages/WebSharePage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | Platform
9 | } from 'react-native'
10 | import WebView from '../common/WebView'
11 |
12 | const NAV_HEIGHT = 64
13 | class WebSharePage extends Component {
14 | render() {
15 | return (
16 |
21 | )
22 | }
23 | }
24 |
25 | WebSharePage.propTypes = {
26 | url: PropTypes.string
27 | }
28 | WebSharePage.defaultProps = {
29 | url: 'http://bonwechat.com'
30 | }
31 |
32 | export default WebSharePage
33 |
--------------------------------------------------------------------------------
/js/constants/Layout.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @providesModule Layout
3 | * @flow
4 | */
5 |
6 | import {
7 | Dimensions,
8 | Platform,
9 | NativeModules,
10 | } from 'react-native';
11 |
12 | const useDrawerNavigation = Platform.OS === 'android';
13 | // const useDrawerNavigation = false;
14 |
15 | export default {
16 | navigationLayoutRoute: useDrawerNavigation ? 'drawerNavigationLayout' : 'tabNavigationLayout',
17 | statusBarHeight: 20,
18 | window: {
19 | width: Dimensions.get('window').width,
20 | height: Dimensions.get('window').height,
21 | },
22 | contentInset: { top: 64, left: 0, right: 0, bottom: 49 },
23 | navIconSize: { width: 30, marginHorizontal:8, marginVertical: 6}
24 | };
25 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/gitfeed/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.gitfeed;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
5 | import com.learnium.RNDeviceInfo.RNDeviceInfo;
6 | import com.oblador.vectoricons.VectorIconsPackage;
7 | import com.microsoft.codepush.react.CodePush;
8 |
9 | public class MainActivity extends ReactActivity {
10 |
11 | /**
12 | * Returns the name of the main component registered from JavaScript.
13 | * This is used to schedule rendering of the component.
14 | */
15 | @Override
16 | protected String getMainComponentName() {
17 | return "gitfeed";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/js/common/Pagination.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | function Pagination(pag = 100) {
4 | this.pag = pag
5 | this.step = 0
6 | this.loadingStep = 0
7 | this.hasMore = false
8 | }
9 |
10 | Pagination.prototype.reset = function () {
11 | this.step = 0
12 | this.loadingStep = 0
13 | this.hasMore = true
14 | }
15 |
16 | Pagination.prototype.incrLoadingStep = function () {
17 | this.loadingStep += this.pag
18 | }
19 |
20 | Pagination.prototype.decrLoadingStep = function () {
21 | this.loadingStep -= this.pag
22 | }
23 |
24 | Pagination.prototype.incrStep = function () {
25 | this.step += this.pag
26 | }
27 |
28 | Pagination.prototype.close = function () {
29 | this.hasMore = false
30 | }
31 |
32 | export default Pagination
--------------------------------------------------------------------------------
/js/reducers/search.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import * as sch from '../actions/search'
4 |
5 | const defaultState = {
6 | hotTags: ['react-native', 'redux', 'parse-server'],
7 | history: [],
8 | searchText: '',
9 | }
10 |
11 | export const search = (state = defaultState, action) => {
12 | switch (action.type) {
13 | case sch.GET_HOT_TAGS: {
14 | if (action.tags) {
15 | return { ...state, hotTags: action.tags }
16 | }
17 | }
18 | case sch.GET_SEARCH_HISTORY:
19 | return Object.assign({}, state, { history: action.history })
20 | case sch.SEARCH_START:
21 | return Object.assign({}, state, {
22 | searchText: action.searchText,
23 | })
24 | }
25 |
26 | return state
27 | }
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.1'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | maven {
20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21 | url "$rootDir/../node_modules/react-native/android"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/js/actions/installation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Platform = require('Platform');
4 | const Parse = require('parse/react-native');
5 |
6 | async function currentInstallation() {
7 | const installationId = await Parse._getInstallationId();
8 | return new Parse.Installation({
9 | installationId,
10 | appName: 'gitfeed',
11 | deviceType: Platform.OS,
12 | // TODO: Get this information from the app itself
13 | appIdentifier: Platform.OS === 'ios' ? 'com.bonyiyan.gitfeed' : 'com.bonyiyan.gitfeed',
14 | });
15 | }
16 |
17 | async function updateInstallation(updates) {
18 | const installation = await currentInstallation();
19 | await installation.save(updates);
20 | }
21 |
22 | module.exports = { currentInstallation, updateInstallation };
23 |
--------------------------------------------------------------------------------
/js/reducers/login.js:
--------------------------------------------------------------------------------
1 | import * as lg from '../actions/login'
2 |
3 |
4 | /**
5 | *
6 | * @type {{user: {username: null, token: null, pwd: null, avatar: null, userId: null, url: null}}}
7 | */
8 | const initialLogin = {
9 | user: {
10 | login: null,
11 | token: null,
12 | pwd: null,
13 | avatar: null,
14 | userId: null,
15 | url: null,
16 | },
17 | loading: false
18 | }
19 |
20 | export const login = (state = initialLogin, action) => {
21 | switch (action.type) {
22 | case lg.USER_LOGIN_SUCCESS:
23 | case lg.USER_ONBOARD:
24 | return { ...state, user: { ...state.user, ...action.user }, loading: false}
25 | case lg.USER_ONBOARD_CHECKING:
26 | return { ...state, loading: true }
27 | }
28 |
29 | return state
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/ios/gitfeedTests/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 |
--------------------------------------------------------------------------------
/js/common/BlurView/BlurView.ios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react';
7 |
8 | import {
9 | View,
10 | } from 'react-native';
11 | import { BlurView as RNBlurView } from 'react-native-blur'
12 |
13 | export default class BlurView extends Component {
14 | static propTypes = {
15 | tint: PropTypes.oneOf(['light', 'xlight', 'dark']).isRequired,
16 | intensity: PropTypes.number.isRequired,
17 | ...View.propTypes,
18 | };
19 |
20 | static defaultProps = {
21 | tint: 'light',
22 | intensity: 100,
23 | }
24 |
25 | render() {
26 | const { tint, intensity } = this.props
27 |
28 | return (
29 |
34 | {this.props.children}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ios/Classes/GFDiskCacheManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // GFDiskCacheManager.m
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 16/1/19.
6 | // Copyright © 2016年 Facebook. All rights reserved.
7 | //
8 |
9 | #import "GFDiskCacheManager.h"
10 | #import "RCTBridge.h"
11 | #import "RCTConvert.h"
12 | #import "GFWebResourceInterceptor.h"
13 | #import "GFWebResourceCache.h"
14 | #import "DXFileManager.h"
15 |
16 | @implementation GFDiskCacheManager
17 |
18 | RCT_EXPORT_MODULE();
19 |
20 | RCT_EXPORT_METHOD(diskCacheCost:(RCTResponseSenderBlock)callback) {
21 | [[GFWebResourceInterceptor globalWebResourceInterceptor].cache cacheCost:^(NSUInteger fileSize) {
22 | callback(@[@(fileSize)]);
23 | }];
24 | }
25 |
26 | RCT_EXPORT_METHOD(clearDiskCache:(RCTResponseSenderBlock)callback) {
27 | [[GFWebResourceInterceptor globalWebResourceInterceptor].cache clearCache:^(NSUInteger fileSize) {
28 | callback(@[@(fileSize)]);
29 | }];
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'gitfeed'
2 |
3 | include ':app'
4 | include ':react-native-blur'
5 | project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-blur/android')
6 | include ':react-native-push-notification'
7 | project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android')
8 | include ':react-native-device-info'
9 | project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
10 | include ':react-native-vector-icons'
11 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
12 | include ':react-native-code-push'
13 | project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
14 |
--------------------------------------------------------------------------------
/ios/Classes/Additions/NSString+GFAdditions.m:
--------------------------------------------------------------------------------
1 | #import "NSString+GFAdditions.h"
2 | #import
3 |
4 | @implementation NSString (GFAdditions)
5 |
6 | + (NSString *)jm_stringFromDigest:(uint8_t *)digest length:(int)length {
7 | NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:length * 2];
8 | for (int i = 0; i < length; i++) {
9 | [ms appendFormat: @"%02x", (int)digest[i]];
10 | }
11 |
12 | return [ms copy];
13 | }
14 |
15 | - (NSData *)jm_prehashData {
16 | const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
17 | return [NSData dataWithBytes:cstr length:self.length];
18 | }
19 |
20 | - (NSString *)jm_MD5Digest {
21 | NSData *preHashData = [self jm_prehashData];
22 | uint8_t digest[CC_MD5_DIGEST_LENGTH];
23 | CC_MD5(preHashData.bytes, (CC_LONG)preHashData.length, digest);
24 |
25 | return [[self class] jm_stringFromDigest:digest length:CC_MD5_DIGEST_LENGTH];
26 | }
27 |
28 | @end
29 |
30 |
--------------------------------------------------------------------------------
/js/common/Util.js:
--------------------------------------------------------------------------------
1 | //export const debounce = (func, wait = 1000, immediate = true) => {
2 | // var timeout;
3 | // return function() {
4 | // var context = this, args = arguments;
5 | // var later = function() {
6 | // timeout = null;
7 | // if (!immediate) func.apply(context, args);
8 | // };
9 | // var callNow = immediate && !timeout;
10 | // clearTimeout(timeout);
11 | // timeout = setTimeout(later, wait);
12 | // if (callNow) func.apply(context, args);
13 | // };
14 | //}
15 |
16 | exports.debounce = (func, wait = 1000, immediate = true) => {
17 | var timeout;
18 | return function() {
19 | var context = this, args = arguments;
20 | var later = function() {
21 | timeout = null;
22 | if (!immediate) func.apply(context, args);
23 | };
24 | var callNow = immediate && !timeout;
25 | clearTimeout(timeout);
26 | timeout = setTimeout(later, wait);
27 | if (callNow) func.apply(context, args);
28 | };
29 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useDeprecatedNdk=true
21 |
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceInterceptorSettings.m:
--------------------------------------------------------------------------------
1 | #import "GFWebResourceInterceptorSettings+Internal.h"
2 |
3 | @implementation GFWebResourceInterceptorSettings
4 |
5 | @synthesize version = _version;
6 | @synthesize enabled = _enabled;
7 | @synthesize extensions = _extensions;
8 | @synthesize whitelist = _whitelist;
9 | @synthesize blacklist = _blacklist;
10 | @synthesize cacheMaxAge = _cacheMaxAge;
11 |
12 | + (instancetype)buildDefaultWebResourceInterceptorSettings {
13 | GFWebResourceInterceptorSettings *settings = [GFWebResourceInterceptorSettings new];
14 | settings.version = 0;
15 | settings.enabled = YES;
16 | settings.extensions = @"js,css,md";
17 | settings.whitelist = @[@"github.com"];
18 | settings.blacklist = @[@"www.google-analytics.com"];
19 | settings.cacheMaxAge = 24 * 60 * 60 * 3;
20 |
21 | return settings;
22 | }
23 |
24 | + (instancetype)defaultInterceptorSettings {
25 | return [[self class] buildDefaultWebResourceInterceptorSettings];
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/js/pages/FavPage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | Text,
10 | View,
11 | } from 'react-native'
12 | import { connect } from 'react-redux'
13 | import { getFavPages } from '../actions/fav'
14 | import GridPage from '../common/GridPage'
15 |
16 | class FavPage extends Component {
17 | constructor(props) {
18 | super(props)
19 | }
20 |
21 | componentDidMount() {
22 | this.props.dispatch(getFavPages())
23 | }
24 |
25 | render() {
26 | return
31 | }
32 | }
33 |
34 | FavPage.propTypes = {
35 | dataSource: PropTypes.array.isRequired
36 | }
37 | FavPage.defaultProps = {
38 | dataSource: []
39 | }
40 |
41 | const mapStateToProps = state => {
42 | return {
43 | dataSource: state.favs
44 | }
45 | }
46 |
47 | export default connect(mapStateToProps)(FavPage)
--------------------------------------------------------------------------------
/js/pages/RecentPage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | Text,
10 | View,
11 | } from 'react-native'
12 | import { connect } from 'react-redux'
13 | import { getRecent } from '../actions/recent'
14 | import GridPage from '../common/GridPage'
15 |
16 | class RecentPage extends Component {
17 | constructor(props) {
18 | super(props)
19 | }
20 |
21 | componentDidMount() {
22 | this.props.dispatch(getRecent())
23 | }
24 |
25 | render() {
26 | return
32 | }
33 | }
34 |
35 | RecentPage.propTypes = {
36 | dataSource: PropTypes.array.isRequired
37 | }
38 | RecentPage.defaultProps = {
39 | dataSource: []
40 | }
41 |
42 | const mapStateToProps = state => {
43 | return {
44 | dataSource: state.recents
45 | }
46 | }
47 |
48 | export default connect(mapStateToProps)(RecentPage)
49 |
--------------------------------------------------------------------------------
/js/common/BlurView/BlurView.android.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react';
7 |
8 | import {
9 | View,
10 | } from 'react-native';
11 |
12 | import deprecatedPropType from 'react-native/Libraries/Utilities/deprecatedPropType';
13 |
14 | export default class BlurView extends Component {
15 | static propTypes = {
16 | tintEffect: deprecatedPropType(
17 | PropTypes.string,
18 | 'Use the `tint` prop instead.'
19 | ),
20 | tint: PropTypes.oneOf(['light', 'default', 'dark']),
21 | ...View.propTypes,
22 | };
23 |
24 | render() {
25 | let { tint } = this.props;
26 |
27 | let backgroundColor;
28 | if (tint === 'dark') {
29 | backgroundColor = 'rgba(0,0,0,0.5)';
30 | } else if (tint === 'light') {
31 | backgroundColor = 'rgba(255,255,255,0.7)';
32 | } else {
33 | backgroundColor = 'rgba(255,255,255,0.4)';
34 | }
35 |
36 | return (
37 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/AppIcons/ios/README.md:
--------------------------------------------------------------------------------
1 | ## iTunesArtwork & iTunesArtwork@2x (App Icon) file extension:
2 |
3 | PNG extension is prepended to these two files -
4 |
5 | While Apple suggested to omit the extension for these files,
6 | the '.png' extension is actually required for iTunesConnect submission.
7 |
8 | This is done for you so you don't have to.
9 |
10 | However, for Ad_hoc or Enterprise distirbution, the extension should be removed
11 | from the files before adding to XCode to avoid error.
12 |
13 | refs: https://developer.apple.com/library/ios/qa/qa1686/_index.html
14 |
15 | ## iTunesArtwork & iTunesArtwork@2x (App Icon) transparency handling:
16 |
17 | As images with alpha channels or transparencies cannot be set as an application's icon on
18 | iTunesConnect, all transparent pixels in your images will be converted into
19 | solid blacks.
20 |
21 | To achieve the best result, you're advised to adjust the transparency settings
22 | in your source files before converting them with makeAppIcon.
23 |
24 | refs: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/AppIcons.html
25 |
--------------------------------------------------------------------------------
/js/actions/search.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import {
4 | getSearchHistory,
5 | insertSearchRecord,
6 | removeSearchRecord
7 | } from '../services/Storage'
8 |
9 | export const GET_HOT_TAGS = 'GET_HOT_TAGS'
10 | export const GET_SEARCH_HISTORY = 'GET_SEARCH_HISTORY'
11 | export const SEARCH_START = 'SEARCH_START'
12 |
13 | export const getHotTags = () => {
14 | // Async get the TAGS
15 | return dispatch => {
16 | dispatch({
17 | type: GET_HOT_TAGS,
18 | })
19 | }
20 | }
21 |
22 | export const searchSearchHistory = async() => {
23 | const history = await getSearchHistory()
24 | return {
25 | type: GET_SEARCH_HISTORY,
26 | history
27 | }
28 | }
29 |
30 | export const enqueueSearchHistory = async(his) => {
31 | await insertSearchRecord(his)
32 | return await searchSearchHistory()
33 | }
34 |
35 | export const removeSearchHistory = async(his) => {
36 | await removeSearchRecord(his)
37 | return await searchSearchHistory()
38 | }
39 |
40 | /**
41 | *
42 | * from: 'TAG', 'HISTORY', 'INPUT'
43 | */
44 | export const startSearch = (text, from = 'INPUT')=> {
45 | return {
46 | type: SEARCH_START,
47 | searchText: text,
48 | from: from
49 | }
50 | }
--------------------------------------------------------------------------------
/js/services/xFetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | const checkIfErrorOccurs = res => {
6 | return {
7 | code: res.status,
8 | res
9 | }
10 | }
11 |
12 | const TIME_OUT = 15000
13 |
14 | async function xFetch(path, headerOptions, ops = { noParse: false }) {
15 | const normalFetch = fetch(path, headerOptions)
16 | if (ops.noParse) {
17 | return timeoutPromise(TIME_OUT, normalFetch)
18 | }
19 |
20 | const res = await timeoutPromise(TIME_OUT, normalFetch.then(checkIfErrorOccurs))
21 | const response = await res.res.json()
22 | if (res.code < 300) {
23 | return response
24 | } else {
25 | throw new Error(`${res.code} ${response.message}`)
26 | }
27 | }
28 |
29 | export const timeoutPromise = function timeoutPromise(ms, promise) {
30 | return new Promise((resolve, reject) => {
31 | const timeoutId = setTimeout(() => {
32 | reject(new Error("request time out"))
33 | }, ms);
34 | promise.then(
35 | (res) => {
36 | clearTimeout(timeoutId);
37 | resolve(res);
38 | },
39 | (err) => {
40 | clearTimeout(timeoutId);
41 | reject(err);
42 | }
43 | );
44 | })
45 | }
46 |
47 | export default xFetch
48 |
--------------------------------------------------------------------------------
/js/common/MarkdownView.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | View,
10 | Text,
11 | TouchableOpacity,
12 | } from 'react-native'
13 | import Color from './Colors'
14 | import MDWebView from './markdown'
15 |
16 | class MarkdownView extends Component {
17 | constructor() {
18 | super()
19 | this.state = {
20 | md: ''
21 | }
22 | }
23 |
24 | componentDidMount() {
25 | const self = this
26 | fetch('https://github.com/sindresorhus/github-markdown-css/blob/gh-pages/readme.md')
27 | .then(res => res.text())
28 | .then(md => {
29 | self.setState({ md: md, })
30 | })
31 | .catch(err => console.log('err is', err))
32 | }
33 |
34 |
35 | render() {
36 | const { md } = this.state
37 |
38 | const article = md.slice(md.indexOf('') + 'article>'.length)
39 |
40 | return (
41 |
42 | {article}
43 |
44 | );
45 | }
46 | }
47 |
48 | module.exports = MarkdownView
49 |
50 | module.exports.__cards__ = (define) => {
51 | define('Gray', (state = true, update) =>
52 | )
53 | }
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/js/services/Login.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | import Parse from 'parse/react-native'
6 | import DeviceInfo from 'react-native-device-info'
7 |
8 | const DEFAULT_ID = DeviceInfo.getUniqueID()
9 |
10 | export const getCurrentUser = async() => {
11 | let currentUser = await Parse.User.currentAsync()
12 | if (!currentUser) {
13 | const uqId = DEFAULT_ID
14 | const userQuery = new Parse.Query(Parse.User)
15 | userQuery.equalTo('username', uqId)
16 | currentUser = await userQuery.first()
17 |
18 | if (!currentUser) {
19 | currentUser = await signUpDefaultUser()
20 | } else {
21 | currentUser = Parse.User.logIn(DEFAULT_ID, DEFAULT_ID)
22 | }
23 | }
24 |
25 | return currentUser
26 | }
27 |
28 | const signUpDefaultUser = async() => {
29 | const user = new Parse.User()
30 | user.set('username', DEFAULT_ID)
31 | user.set('password', DEFAULT_ID)
32 | user.set('platform', DeviceInfo.getSystemName())
33 | return await user.signUp()
34 | }
35 |
36 | export const syncUserInfo = async(key, data) => {
37 | if (!key || !data) return
38 |
39 | const currentUser = await getCurrentUser()
40 | currentUser.set(key, data)
41 | return await currentUser.save()
42 | }
43 |
44 | export const getUserInfo = () => {
45 |
46 | }
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceInterceptorSettings.h:
--------------------------------------------------------------------------------
1 | @import Foundation;
2 |
3 |
4 | /**
5 | * Web资源拦截器接口定义
6 | */
7 | @protocol GFWebResourceInterceptorSettings
8 | @required
9 |
10 | /**
11 | * 配置版本,默认配置版本为`0`。
12 | * 当更新拦截器配置时候,检查之前配置版本与传入配置的版本是否一样,
13 | * 如果配置版本一样或者传入配置版本小于已生效的配置版本,则丢弃传入配置。
14 | *
15 | * @discussion
16 | * 如果配置已变更,采用数字自增方式管理配置版本。
17 | */
18 | @property (nonatomic, assign) NSInteger version;
19 |
20 | /**
21 | * 控制是否开启Web资源拦截功能,`YES`为开启,`NO`为关闭。默认实现为开启。
22 | */
23 | @property (nonatomic, assign) BOOL enabled;
24 |
25 | /**
26 | * Web资源拦截支持的后缀名。目前支持`js`和`css`。
27 | */
28 | @property (nonatomic, strong) NSString *extensions;
29 |
30 | /**
31 | * Web资源拦截域白名单。
32 | * 检查当前请求关联的`Host`和`Referer`是否在白名单内,
33 | * 如果不在白名单内,则不会触发资源拦截。
34 | */
35 | @property (nonatomic, strong) NSArray *whitelist;
36 |
37 | /**
38 | * Web资源拦截黑名单。
39 | * 检查当前请求关联的`Host`和`Referer`是否在黑名单内,
40 | * 如果在黑名单内,则不会触发资源拦截。
41 | */
42 | @property (nonatomic, strong) NSArray *blacklist;
43 |
44 | /**
45 | * cache的自动清理时间,默认3天 24 * 60 * 60 * 3
46 | */
47 | @property (nonatomic, assign) NSUInteger cacheMaxAge;
48 |
49 | @end
50 |
51 | /**
52 | * Web资源拦截配置默认实现类。
53 | */
54 | @interface GFWebResourceInterceptorSettings : NSObject
55 |
56 | @end
--------------------------------------------------------------------------------
/js/store/array.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | */
22 |
23 | 'use strict';
24 |
25 | module.exports = store => next => action =>
26 | Array.isArray(action)
27 | ? action.map(next)
28 | : next(action);
29 |
--------------------------------------------------------------------------------
/js/services/LookFetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | const Crypto = require('crypto-js')
6 |
7 | const BASE_URL = "http://bonwechat.com"
8 | //const BASE_URL = "http://localhost:7001"
9 |
10 | const errorMessages = (res) => `${res.status} ${res.statusText}`
11 |
12 | function check401(res) {
13 | if (res.status === 401) {
14 | return Promise.reject(errorMessages(res))
15 | }
16 | return res
17 | }
18 |
19 | function check404(res) {
20 | if (res.status === 404) {
21 | return Promise.reject(errorMessages(res))
22 | }
23 | return res
24 | }
25 |
26 | function jsonParse(res) {
27 | return res.json()
28 | }
29 |
30 | function errorMessageParse(res) {
31 | const { code, msg } = res
32 | if (code > 200) {
33 | return Promise.reject(msg)
34 | }
35 | return res
36 | }
37 |
38 | function xFetch(path, options) {
39 | return fetch(path, options)
40 | .then(check401)
41 | .then(check404)
42 | .then(jsonParse)
43 | .then(errorMessageParse)
44 | }
45 |
46 | function getFetch(path) {
47 | let URL = path
48 | if (!/^http.*/.test(URL)) URL = BASE_URL + path
49 |
50 | return xFetch(URL, {
51 | headers: {
52 | 'WECHATLOOK-HEADER': Crypto.HmacSHA1(URL, 'register.wechatlook_rn.js')
53 | }
54 | })
55 | }
56 |
57 | export default getFetch
58 |
--------------------------------------------------------------------------------
/js/store/analytics.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | */
22 |
23 | 'use strict';
24 |
25 | const track = require('./track');
26 |
27 | module.exports = store => next => action => {
28 | track(action);
29 | return next(action);
30 | };
31 |
--------------------------------------------------------------------------------
/ios/gitfeed/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-40@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-40@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Small@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Small@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-40@2x-1.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-40@3x-1.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-60@3x.png",
49 | "scale" : "3x"
50 | }
51 | ],
52 | "info" : {
53 | "version" : 1,
54 | "author" : "xcode"
55 | }
56 | }
--------------------------------------------------------------------------------
/js/pages/FamousPage.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | View,
7 | ActivityIndicatorIOS,
8 | StyleSheet,
9 | ScrollView,
10 | TouchableHighlight,
11 | Image,
12 | TouchableOpacity,
13 | ListView,
14 | Linking,
15 | Dimensions
16 | } from 'react-native'
17 | import Layout from '../constants/Layout'
18 | import RepoCell from '../common/RepoCell'
19 | import GHRefreshListView from '../common/GHRefreshListView'
20 | import { withNavigation } from '@exponent/ex-navigation'
21 | import { UserListView } from '../common/DetailListView'
22 |
23 | @withNavigation
24 | class FamousPage extends Component {
25 | static route = {
26 | navigationBar: {
27 | title: 'Popular guys'
28 | },
29 | }
30 |
31 | render() {
32 | const path = '/search/users?q=location:USA&sort=followers'
33 | return (
34 |
35 | v.items}
38 | navigator={this.props.navigator}
39 | />
40 |
41 | )
42 | }
43 | }
44 |
45 | FamousPage.propTypes = {
46 | showcase: React.PropTypes.object,
47 | }
48 | FamousPage.defaultProps = {}
49 |
50 | export default FamousPage
51 |
52 | var styles = StyleSheet.create({
53 | container: {
54 | flex: 1
55 | },
56 | })
--------------------------------------------------------------------------------
/js/pages/NotificationPage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | Platform,
9 | View,
10 | Linking,
11 | } from 'react-native'
12 | import { getRequest } from '../services/GithubServices'
13 | import AlertStyle from '../constants/Alerts'
14 |
15 | class NotificationPage extends Component {
16 | state = {
17 | notifications: []
18 | }
19 |
20 | componentDidMount() {
21 | this.getNotifications()
22 | }
23 |
24 | async getNotifications() {
25 | try {
26 | const notifications = await geatRequest('/notifications')
27 | this.setState({ notifications, })
28 | } catch (err) {
29 | this.handleError(err)
30 | }
31 | }
32 |
33 | handleError(err) {
34 | if (~err.message.indexOf('authentication')) {
35 | const { navigator } = this.props
36 | navigator.showLocalAlert('You need to login first', AlertStyle.warning)
37 | setTimeout(_ => navigator.push('Login', {
38 | didLogin: this.getNotifications.bind(this)
39 | }), 2000)
40 | }
41 | }
42 |
43 | render() {
44 | console.log('this.notifications is', this.state.notifications)
45 | return
46 | }
47 | }
48 |
49 | NotificationPage.propTypes = {}
50 | NotificationPage.defaultProps = {}
51 |
52 | export default NotificationPage
53 |
54 |
55 |
--------------------------------------------------------------------------------
/js/actions/login.js:
--------------------------------------------------------------------------------
1 | import { basicLogin, getUserInfo } from '../services/GithubServices'
2 | import { saveUser, getCurrentUser } from '../services/Storage'
3 |
4 | export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
5 | export const USER_ONBOARD = 'USERG_ONBOARD'
6 | export const USER_ONBOARD_CHECKING = 'USER_ONBOARD_CHECKING'
7 |
8 | export const onboard = async(user) => {
9 | const savedUser = await saveUser(user)
10 | return {
11 | type: USER_ONBOARD,
12 | user: savedUser
13 | }
14 | }
15 |
16 | export const getUser = () => {
17 | return dispatch => {
18 | dispatch({
19 | type: USER_ONBOARD_CHECKING
20 | })
21 | getCurrentUser()
22 | .then(user => {
23 | dispatch({
24 | type: USER_ONBOARD,
25 | user,
26 | })
27 | })
28 | }
29 | }
30 |
31 | export const login = async(user) => {
32 | if (user.token) {
33 | await saveUser(user)
34 | return {
35 | type: USER_LOGIN_SUCCESS,
36 | user
37 | }
38 | }
39 |
40 | return null
41 |
42 | // TODO: when user enter his profile then refresh it
43 | const detailUser = await getUserInfo(login)
44 | user = {
45 | ...user,
46 | avatar: detailUser.avatar_url,
47 | userId: detailUser.id,
48 | url: detailUser.url
49 | }
50 |
51 | return {
52 | type: USER_LOGIN_SUCCESS,
53 | user
54 | }
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/js/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import {
2 | createNavigationEnabledStore,
3 | NavigationActions
4 | } from '@exponent/ex-navigation'
5 | import { applyMiddleware, createStore } from 'redux'
6 | import thunk from 'redux-thunk'
7 | import promise from './promise'
8 | import array from './array'
9 | import analytics from './analytics'
10 | import reducers from '../reducers'
11 | import createLogger from 'redux-logger'
12 | import Router from '../navigation/Router'
13 |
14 | const createStoreWithNavigation = createNavigationEnabledStore({
15 | createStore,
16 | navigationStateKey: 'navigation',
17 | })
18 |
19 | const isDebuggingInChrome = __DEV__ && !!window.navigator.userAgent
20 | const logger = createLogger({
21 | predicate: (getState, action) => isDebuggingInChrome,
22 | collapsed: true,
23 | duration: true,
24 | })
25 |
26 | let store = createStoreWithNavigation(reducers, applyMiddleware(thunk, promise, array, analytics))
27 | if (__DEV__) {
28 | store = createStoreWithNavigation(reducers, applyMiddleware(thunk, promise, array, analytics, logger))
29 | }
30 |
31 | if (isDebuggingInChrome) {
32 | window.store = store
33 | }
34 |
35 | export default store
36 | export const goToPage = (pageName, params) => {
37 | let navigatorUID = store.getState().navigation.currentNavigatorUID;
38 | store.dispatch(NavigationActions.push(navigatorUID, Router.getRoute(pageName, params)))
39 | }
--------------------------------------------------------------------------------
/js/_Playgroun.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var React = require('React')
4 | var View = require('View')
5 | import RepDetailPage from './pages/RepoDetailPage'
6 | import IssueDetailPage from './pages/IssueDetailPage'
7 |
8 | class Playground extends React.Component {
9 | constructor() {
10 | super()
11 | const content = []
12 | const define = (name, render) => {
13 | content.push()
14 | }
15 |
16 | // var Module = require('./common/MarkdownView')
17 | // var Module = require('./common/SearchHistory')
18 | // var Module = require('./common/IconCell')
19 |
20 | // Module.__cards__(define)
21 | this.state = { content }
22 | }
23 |
24 | render() {
25 | return (
26 | //
27 | //
28 |
29 |
30 | )
31 | }
32 | }
33 |
34 | class Example extends React.Component {
35 | state = {
36 | inner: null
37 | }
38 |
39 | render() {
40 | const content = this.props.render(this.state.inner, (inner) => this.setState({ inner }))
41 | return (
42 |
43 | {content}
44 |
45 | )
46 |
47 | }
48 | }
49 |
50 | module.exports = Playground
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gitfeed",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-native/local-cli/cli.js start",
7 | "test": "jest"
8 | },
9 | "dependencies": {
10 | "@exponent/ex-navigation": "^2.4.0",
11 | "@exponent/react-native-fade-in-image": "^1.1.1",
12 | "@exponent/react-native-read-more-text": "^1.0.0",
13 | "babel-preset-react-native-stage-0": "^1.0.1",
14 | "base-64": "^0.1.0",
15 | "crypto-js": "^3.1.8",
16 | "lodash": "^4.17.2",
17 | "moment": "^2.17.1",
18 | "parse": "^1.9.2",
19 | "react": "~15.3.2",
20 | "react-native": "^0.37.x",
21 | "react-native-blur": "^1.2.0",
22 | "react-native-code-push": "^1.16.1-beta",
23 | "react-native-device-info": "^0.9.7",
24 | "react-native-popup-menu": "^0.6.1",
25 | "react-native-push-notification": "^2.2.1",
26 | "react-native-scrollable-tab-view": "^0.7.0",
27 | "react-native-swiper": "^1.5.4",
28 | "react-native-vector-icons": "^3.0.0",
29 | "react-redux": "^4.4.5",
30 | "redux": "^3.6.0",
31 | "redux-logger": "^2.6.1",
32 | "redux-thunk": "^2.1.0"
33 | },
34 | "jest": {
35 | "preset": "react-native"
36 | },
37 | "devDependencies": {
38 | "babel-jest": "17.0.2",
39 | "babel-preset-react-native": "1.9.0",
40 | "jest": "17.0.3",
41 | "react-test-renderer": "15.3.1"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/js/navigation/Router.js:
--------------------------------------------------------------------------------
1 | import {
2 | createRouter,
3 | } from '@exponent/ex-navigation'
4 |
5 | import RepoDetailPage from '../pages/RepoDetailPage'
6 | import HomePage from '../pages/HomePage'
7 | import IssueDetailPage from '../pages/IssueDetailPage'
8 | import SearchPage from '../pages/SearchPage'
9 | import WebView from '../common/WebView'
10 | import LoginPage from '../pages/LoginPage'
11 | import Playground from '../Playground'
12 | import UserDetailPage from '../pages/UserDetailPage'
13 | import NotificationPage from '../pages/NotificationPage'
14 | import PersonPage from '../pages/PersonPage'
15 | import TrendPage from '../pages/TrendPage'
16 | import ShowcasePage from '../pages/ShowcasePage'
17 | import FamousPage from '../pages/FamousPage'
18 | import { RepoListView, UserListView } from '../common/DetailListView'
19 |
20 | export default createRouter(() => ({
21 | RepoDetail: () => RepoDetailPage,
22 | Home: () => HomePage,
23 | IssueDetail: () => IssueDetailPage,
24 | Search: () => SearchPage,
25 | Web: () => WebView,
26 | Login: () => LoginPage,
27 | Playground: () => Playground,
28 | UserDetail: () => UserDetailPage,
29 | UserList: () => UserListView, // { url: 'https://api.github.com/users/getify/followers?per_page=100' }
30 | RepoList: () => RepoListView,
31 | Notification: () => NotificationPage,
32 | Me: () => PersonPage,
33 | Trend: () => TrendPage,
34 | Showcase: () => ShowcasePage,
35 | Famous: () => FamousPage
36 | }))
--------------------------------------------------------------------------------
/android/app/src/main/java/com/gitfeed/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.gitfeed;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 |
6 | import com.facebook.react.ReactApplication;
7 | import com.cmcewen.blurview.BlurViewPackage;
8 | import com.facebook.react.ReactInstanceManager;
9 | import com.facebook.react.ReactNativeHost;
10 | import com.facebook.react.ReactPackage;
11 | import com.facebook.react.shell.MainReactPackage;
12 | import com.facebook.soloader.SoLoader;
13 |
14 | import java.util.Arrays;
15 | import java.util.List;
16 |
17 | public class MainApplication extends Application implements ReactApplication {
18 |
19 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
20 |
21 | @Override
22 | protected String getJSBundleFile() {
23 | return CodePush.getJSBundleFile();
24 | }
25 |
26 | @Override
27 | protected boolean getUseDeveloperSupport() {
28 | return BuildConfig.DEBUG;
29 | }
30 |
31 | @Override
32 | protected List getPackages() {
33 | return Arrays.asList(
34 | new MainReactPackage(),
35 | new BlurViewPackage()
36 | );
37 | }
38 | };
39 |
40 | @Override
41 | public ReactNativeHost getReactNativeHost() {
42 | return mReactNativeHost;
43 | }
44 |
45 | @Override
46 | public void onCreate() {
47 | super.onCreate();
48 | SoLoader.init(this, /* native exopackage */ false);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/js/Playground.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | View,
7 | Text,
8 | StyleSheet,
9 | ScrollView,
10 | TouchableHighlight,
11 | Image,
12 | TouchableOpacity,
13 | } from 'react-native'
14 | import { NavigationStyles } from '@exponent/ex-navigation'
15 |
16 | class Playground extends React.Component {
17 | constructor() {
18 | super()
19 | const content = []
20 | const define = (name, render) => {
21 | content.push()
22 | }
23 |
24 | // var Module = require('./common/MarkdownView')
25 | // var Module = require('./common/SearchHistory')
26 | // var Module = require('./common/IconCell')
27 |
28 | // Module.__cards__(define)
29 | this.state = { content }
30 | }
31 |
32 | render() {
33 | return (
34 | //
35 | //
36 |
37 |
38 | )
39 | }
40 | }
41 |
42 | class Example extends React.Component {
43 | state = {
44 | inner: null
45 | }
46 |
47 | render() {
48 | const content = this.props.render(this.state.inner, (inner) => this.setState({ inner }))
49 | return (
50 |
51 | {content}
52 |
53 | )
54 |
55 | }
56 | }
57 |
58 | module.exports = Playground
59 |
--------------------------------------------------------------------------------
/js/setup.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | AsyncStorage
5 | } from 'react'
6 | import BlurView from './common/BlurView'
7 | import WLApp from './WLApp'
8 | import { Provider } from 'react-redux'
9 | import store from './store/configureStore'
10 | import Parse from 'parse/react-native'
11 | import { serverURL } from './env'
12 | //import Playground from './Playground'
13 | import {
14 | NavigationContext,
15 | NavigationProvider,
16 | } from '@exponent/ex-navigation'
17 | import Router from './navigation/Router'
18 | import {
19 | MenuContext,
20 | } from 'react-native-popup-menu'
21 |
22 | const navigationContext = new NavigationContext({
23 | router: Router,
24 | store: store,
25 | })
26 |
27 | const setup = () => {
28 | // AsyncStorage.clear()
29 |
30 | // something for initialize
31 | // Parse.initialize('oss-f8-app-2016')
32 | // Parse.serverURL = `${serverURL}/parse`
33 |
34 | return () => {
35 | return (
36 |
37 | {/**/}
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 | }
47 |
48 | global.LOG = (...args) => {
49 | console.log('/------------------------------\\')
50 | console.log(...args)
51 | console.log('\\------------------------------/')
52 | return args[args.length - 1]
53 | }
54 |
55 | export default setup
56 |
--------------------------------------------------------------------------------
/js/common/RoundTagButton.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | View,
10 | Text,
11 | TouchableOpacity,
12 | } from 'react-native'
13 | import Color from '../constants/Colors'
14 |
15 | class RoundTabButton extends Component {
16 | constructor(props) {
17 | super(props)
18 | }
19 |
20 | render() {
21 | const { color, text, style } = this.props
22 | return (
23 |
26 |
27 | {text}
28 |
29 |
30 | )
31 | }
32 | }
33 |
34 | RoundTabButton.propTypes = {
35 | color: PropTypes.string,
36 | text: PropTypes.string.isRequired,
37 | onSelect: PropTypes.func
38 | }
39 |
40 | RoundTabButton.defaultProps = {
41 | color: Color.lightBlack,
42 | text: '金馆长'
43 | }
44 |
45 | module.exports = RoundTabButton
46 |
47 | module.exports.__cards__ = (define) => {
48 | define('Gray', (state = true, update) =>
49 | console.log('hellow')} />)
50 | }
51 |
52 | var styles = StyleSheet.create({
53 | container: {
54 | flexDirection: 'row',
55 | alignItems: 'center',
56 | justifyContent: 'center',
57 | height: 35,
58 | borderRadius: 17,
59 | paddingLeft: 12,
60 | paddingRight: 12,
61 | borderWidth: 1,
62 | },
63 | text: {}
64 | })
--------------------------------------------------------------------------------
/js/common/GridView.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import {
5 | AppRegistry,
6 | View,
7 | StyleSheet,
8 | ListView,
9 | } from 'react-native';
10 |
11 | export default React.createClass({
12 | groupItems: function(items, itemsPerRow) {
13 | var itemsGroups = [];
14 | var group = [];
15 | items.forEach(function(item) {
16 | if (group.length === itemsPerRow) {
17 | itemsGroups.push(group);
18 | group = [item];
19 | } else {
20 | group.push(item);
21 | }
22 | });
23 |
24 | if (group.length > 0) {
25 | itemsGroups.push(group);
26 | }
27 |
28 | return itemsGroups;
29 | },
30 | renderGroup: function(group, sectionID, rowID) {
31 | var that = this;
32 | var items = group.map(function(item, index) {
33 | return that.props.renderItem(item, index, rowID);
34 | });
35 | return (
36 |
40 | {items}
41 |
42 | );
43 | },
44 | render: function() {
45 | var groups = this.groupItems(this.props.items, this.props.itemsPerRow);
46 | var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
47 | return ();
52 | },
53 | });
54 |
55 |
56 | var styles = StyleSheet.create({
57 | group: {
58 | flexDirection: 'row',
59 | alignItems: 'center',
60 | justifyContent: 'flex-start',
61 | overflow: 'hidden'
62 | }
63 | });
--------------------------------------------------------------------------------
/js/common/ReadMore.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | View,
8 | Image,
9 | TouchableHighlight,
10 | TouchableOpacity
11 | } from 'react-native'
12 | import Colors from '../constants/Colors'
13 | import { Text, LinkText, NormalText, Heading2 } from '../common/F8Text'
14 | import ReadMore from '@exponent/react-native-read-more-text'
15 |
16 |
17 | class RM extends Component {
18 | _renderTruncatedFooter(handlePress) {
19 | return (
20 |
22 | Read more
23 |
24 | )
25 | }
26 |
27 | _renderRevealedFooter(handlePress) {
28 | return (
29 |
31 | Show less
32 |
33 | )
34 | }
35 |
36 | render() {
37 | const { lines, content } = this.props
38 | return (
39 |
43 |
50 | {content}
51 |
52 |
53 | )
54 | }
55 | }
56 |
57 | RM.propTypes = {
58 | lines: PropTypes.number,
59 | }
60 | RM.defaultProps = {
61 | lines: 3
62 | }
63 |
64 | export default RM
65 |
--------------------------------------------------------------------------------
/js/env.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @flow
23 | */
24 |
25 | 'use strict';
26 |
27 | let env
28 | if (__DEV__) {
29 | env = {
30 | testMenuEnabled: true,
31 | // FIXME: 这里只能填写 ip 地址, 安卓模拟器比较 burden
32 | serverURL: 'http://30.10.108.2:8080',
33 | version: '2.0.0',
34 | fontFamily: undefined,
35 | }
36 | } else {
37 | env = {
38 | testMenuEnabled: true,
39 | serverURL: 'https://bonwechat.com:8080',
40 | version: '2.0.0',
41 | fontFamily: undefined,
42 | }
43 | }
44 |
45 | module.exports = env
46 |
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceCache.h:
--------------------------------------------------------------------------------
1 | @import Foundation;
2 |
3 | /**
4 | * Web资源缓存管理类
5 | */
6 | @interface GFWebResourceCache : NSObject
7 |
8 | @property (nonatomic, strong, readonly) NSString *cachePath;
9 |
10 | /**
11 | * 检查是否有命中的缓存索引,用于提升检索效率,减少IO操作。
12 | *
13 | * @param key 缓存对应唯一的key
14 | *
15 | * @return 返回`YES`标示本地存在对应的缓存,反之则`NO`
16 | */
17 | - (BOOL)hasCacheForKey:(NSString *)key;
18 |
19 | /**
20 | * 通过传入的key创建缓存检索索引。
21 | *
22 | * @param key 缓存对应唯一的key
23 | */
24 | - (void)addCacheIndexForKey:(NSString *)key;
25 |
26 | /**
27 | * 移除该key关联的缓存检索索引。
28 | *
29 | * @param key 缓存对应唯一的key
30 | */
31 | - (void)removeCacheIndexForKey:(NSString *)key;
32 |
33 | /**
34 | * 清除cache的index table和cache file
35 | *
36 | */
37 | - (void)clearCache:(void (^)(NSUInteger fileSize))completionBlock;
38 |
39 | /**
40 | * 查看cache了多少大小
41 | *
42 | */
43 | - (void)cacheCost:(void (^)(NSUInteger fileSize))completionBlock;
44 |
45 | /**
46 | * 通过缓存key获取关联的数据。
47 | *
48 | * @param key 缓存对应唯一的key
49 | * @param url 请求URL
50 | * @param response 响应头
51 | *
52 | * @return 返回本地缓存的数据,如果返回为`nil`,则需要手工移除该key关联的缓存索引
53 | */
54 | - (NSData *)cacheDataForKey:(NSString *)key url:(NSURL *)url response:(NSURLResponse **)response;
55 |
56 | /**
57 | * 通过关联的key存储缓存数据
58 | *
59 | * @param data 数据
60 | * @param response 响应头
61 | * @param key 缓存对应唯一的key
62 | * @param completionBlock 完成回调
63 | */
64 | - (void)storeCacheData:(NSData *)data
65 | response:(NSURLResponse *)response
66 | forKey:(NSString *)key
67 | completionBlock:(void (^)(BOOL succeed))completionBlock;
68 |
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/ios/Classes/H5/GFWebResourceInterceptor.h:
--------------------------------------------------------------------------------
1 | @import Foundation;
2 | #import "GFWebResourceInterceptorSettings+Internal.h"
3 |
4 | @class GFWebResourceCache;
5 |
6 | /**
7 | * Web资源拦截器实现类
8 | */
9 | @interface GFWebResourceInterceptor : NSObject
10 |
11 | /**
12 | * 全局共享的Web资源拦截器
13 | *
14 | * @return 返回全局共享的Web资源拦截器
15 | */
16 | + (instancetype)globalWebResourceInterceptor;
17 |
18 | /**
19 | * 设置自定义的全局共享Web资源拦截器。当传入`nil`时,则清空全局的Web资源拦截器。
20 | *
21 | * @param interceptor 自定义拦截器或为`nil`
22 | */
23 | + (void)setGlobalWebResourceInterceptor:(GFWebResourceInterceptor *)interceptor;
24 |
25 | /**
26 | * 拦截器设置
27 | */
28 | @property (nonatomic, strong, readonly) id settings;
29 |
30 | /**
31 | * 拦截器资源缓存
32 | */
33 | @property (nonatomic, strong, readonly) GFWebResourceCache *cache;
34 |
35 | /**
36 | * 配置默认Web资源拦截器的设置
37 | */
38 | - (void)setupDefaultWebResourceInterceptorSettings;
39 |
40 | /**
41 | * 检查传入的主机名是否在白名单内
42 | *
43 | * @param host 主机名
44 | *
45 | * @return 返回`YES`则表示在白名单内,`NO`则表示不在白名单内
46 | */
47 | - (BOOL)isWhitelistHost:(NSString *)host;
48 |
49 | /**
50 | * 检查传入的资源路径是否在黑名单内
51 | *
52 | * @param path 请求资源路径
53 | *
54 | * @return 返回`YES`则表示在黑名单内,`NO`则表示不在黑名单内
55 | */
56 | - (BOOL)isBlacklistWithRequestPath:(NSString *)path;
57 |
58 | /**
59 | * 检查是否支持传入的文件后缀名
60 | *
61 | * @param extension 文件后缀名
62 | *
63 | * @return 返回`YES`则表示支持该后缀名,反之则为`NO`
64 | */
65 | - (BOOL)isSupportedPathExtension:(NSString *)extension;
66 |
67 | /**
68 | * 更新Web资源拦截器设置
69 | *
70 | * @param settings Web资源拦截器设置
71 | */
72 | - (void)updateInterceptorSettings:(id)settings;
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/js/pages/HomePage.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react'
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Navigator,
7 | TouchableOpacity,
8 | Dimensions
9 | } from 'react-native'
10 | import GHRefreshListView from '../common/GHRefreshListView'
11 | import Layout from '../constants/Layout'
12 | import { connect } from 'react-redux'
13 | import { create } from '../common/F8StyleSheet'
14 | import GHCell from '../common/GHEventCell'
15 | import { SearchButton } from '../common/SearchView'
16 | import { goToPage } from '../store/configureStore'
17 |
18 | class HomePage extends Component {
19 | static route = {
20 | navigationBar: {
21 | title(params, props) {
22 | return goToPage('Search')}/>
23 | },
24 | },
25 | }
26 |
27 | render() {
28 | const { user } = this.props
29 |
30 | return (
31 |
38 | )
39 | }
40 |
41 | renderRow(rowData, sectionID, rowID, highlightRow) {
42 | return (
43 |
44 | )
45 | }
46 | }
47 |
48 | HomePage.propTypes = {
49 | navigator: PropTypes.object,
50 | user: PropTypes.object
51 | }
52 | HomePage.defaultProps = {
53 | user: { login: '' }
54 | }
55 |
56 | const select = state => {
57 | return {
58 | user: state.login.user,
59 | }
60 | }
61 |
62 | export default connect(select)(HomePage)
63 |
--------------------------------------------------------------------------------
/ios/Classes/DXRNUtils.m:
--------------------------------------------------------------------------------
1 | //
2 | // DXRNUtils.m
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/11/18.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import "DXRNUtils.h"
10 | #import "MobClick.h"
11 |
12 | static NSString * const kAppId = @"1079873993";
13 |
14 | @implementation DXRNUtils
15 |
16 | @synthesize bridge = _bridge;
17 |
18 | RCT_EXPORT_MODULE()
19 |
20 | RCT_EXPORT_METHOD(clearCookies:(RCTResponseSenderBlock)callback) {
21 | NSHTTPCookieStorage *cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
22 | for (NSHTTPCookie *cookie in [cookieStore cookies]) {
23 | [cookieStore deleteCookie:cookie];
24 | }
25 |
26 | callback(@[[NSNull null]]);
27 | }
28 |
29 | RCT_EXPORT_METHOD(trackClick:(nonnull NSString *)eventName attributes:(NSDictionary *)atr) {
30 | [MobClick event:eventName attributes:atr];
31 | }
32 |
33 | RCT_EXPORT_METHOD(appInfo:(RCTResponseSenderBlock)callback) {
34 | NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
35 | NSString *appBuild = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
36 | NSString *appStoreURL = [[NSString alloc] initWithFormat:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=%@&mt=8", kAppId];
37 | NSString *rateURL = [[NSString alloc] initWithFormat:@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@", kAppId];
38 |
39 | callback(@[
40 | @{@"appVersion": appVersion,
41 | @"appBuild": appBuild,
42 | @"appStoreURL": appStoreURL,
43 | @"rateURL": rateURL
44 | }
45 | ]);
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/js/pages/Languages.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | TouchableOpacity,
10 | Platform,
11 | ScrollView,
12 | SegmentedControlIOS
13 | } from 'react-native'
14 | import { changeLanguage, changeRule } from '../actions/language'
15 | import { connect } from 'react-redux'
16 | import store from '../store/configureStore'
17 | import PickerFilter from '../common/PickerFilter'
18 | import { defaultState } from '../reducers/language'
19 |
20 | class LanguagePicker extends Component {
21 | onChange(main, sub) {
22 | if (main && sub) {
23 | if (main === 'choose sort') {
24 | store.dispatch(changeRule(sub))
25 | }
26 |
27 | if (main === 'choose languages') {
28 | store.dispatch(changeLanguage(sub))
29 | }
30 | }
31 | }
32 |
33 | render() {
34 | const { currentRule, currentLanguage } = this.props
35 | return (
36 |
51 | )
52 | }
53 |
54 | }
55 |
56 | LanguagePicker.propTypes = {}
57 | LanguagePicker.defaultProps = {}
58 |
59 | const select = state => {
60 | return {
61 | currentRule: state.language.currentRule,
62 | currentLanguage: state.language.currentLanguage
63 | }
64 | }
65 |
66 | export default connect(select)(LanguagePicker)
67 |
--------------------------------------------------------------------------------
/js/common/UserCell.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | TouchableOpacity,
10 | TouchableHighlight,
11 | Image,
12 | Platform,
13 | ScrollView,
14 | SegmentedControlIOS
15 | } from 'react-native'
16 | import Colors from '../constants/Colors'
17 | import CStyles from '../constants/Styles'
18 | import { Heading2 } from './F8Text'
19 | import Touchable from './F8Touchable'
20 |
21 | class UserCell extends Component {
22 | openTargetUser() {
23 | const { user, navigator } = this.props
24 | navigator.push('UserDetail', { user })
25 | }
26 |
27 | render() {
28 | const user = this.props.user
29 |
30 | return (
31 |
32 |
33 |
34 |
35 | {user.login}
36 |
37 |
38 |
39 |
40 | )
41 | }
42 | }
43 |
44 | UserCell.propTypes = {
45 | user: React.PropTypes.object,
46 | }
47 | UserCell.defaultProps = {}
48 |
49 | export default UserCell
50 |
51 | const styles = StyleSheet.create({
52 | cellContentView: {
53 | flexDirection: 'column',
54 | },
55 | rowContent: {
56 | flexDirection: 'row',
57 | height: 50,
58 | alignItems: 'center',
59 | },
60 | avatar: {
61 | width: 40,
62 | height: 40,
63 | marginLeft: 15,
64 | backgroundColor: Colors.imagePlaceholder,
65 | },
66 | userName: {
67 | marginLeft: 20,
68 | },
69 | })
--------------------------------------------------------------------------------
/js/pages/TrendPicker.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | TouchableOpacity,
10 | Platform,
11 | ScrollView,
12 | SegmentedControlIOS
13 | } from 'react-native'
14 | import { changeLanguage, changeRule } from '../actions/language'
15 | import { connect } from 'react-redux'
16 | import store from '../store/configureStore'
17 | import PickerFilter from '../common/PickerFilter'
18 | import { defaultState } from '../reducers/language'
19 |
20 | class LanguagePicker extends Component {
21 | onChange(main, sub) {
22 | if (main && sub) {
23 | if (main === 'choose sort') {
24 | store.dispatch(changeRule(sub))
25 | }
26 |
27 | if (main === 'choose languages') {
28 | store.dispatch(changeLanguage(sub))
29 | }
30 | }
31 | }
32 |
33 | render() {
34 | const { currentRule, currentLanguage } = this.props
35 | return (
36 |
51 | )
52 | }
53 |
54 | }
55 |
56 | LanguagePicker.propTypes = {}
57 | LanguagePicker.defaultProps = {}
58 |
59 | const select = state => {
60 | return {
61 | currentRule: state.language.currentRule,
62 | currentLanguage: state.language.currentLanguage
63 | }
64 | }
65 |
66 | export default connect(select)(LanguagePicker)
67 |
--------------------------------------------------------------------------------
/js/pages/ShowcasePage.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | View,
7 | ActivityIndicatorIOS,
8 | StyleSheet,
9 | ScrollView,
10 | TouchableHighlight,
11 | Image,
12 | TouchableOpacity,
13 | ListView,
14 | Linking,
15 | Dimensions
16 | } from 'react-native'
17 | import Layout from '../constants/Layout'
18 | import RepoCell from '../common/RepoCell'
19 | import GHRefreshListView from '../common/GHRefreshListView'
20 | import { withNavigation } from '@exponent/ex-navigation'
21 |
22 | const SHOW_CASE_PATH = 'http://trending.codehub-app.com/v2/showcases'
23 |
24 | @withNavigation
25 | class ShowcasePage extends Component {
26 | static route = {
27 | navigationBar: {
28 | title(props) {
29 | return props.showcase.name
30 | }
31 | },
32 | }
33 |
34 | renderRow(rowData, sectionID, rowID, highlightRow) {
35 | return
36 | }
37 |
38 | render() {
39 | const path = SHOW_CASE_PATH + '/' + this.props.showcase.slug
40 | return (
41 |
42 | v.repositories}
48 | contentOffset={{x:0, y:-Layout.contentInset.top}}
49 | />
50 |
51 | )
52 | }
53 | }
54 |
55 | ShowcasePage.propTypes = {
56 | showcase: React.PropTypes.object,
57 | }
58 | ShowcasePage.defaultProps = {}
59 |
60 | export default ShowcasePage
61 |
62 | var styles = StyleSheet.create({
63 | container: {
64 | flex: 1
65 | },
66 | })
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Github Feed
2 | ---
3 | Yet another Github client written in react-native.
4 |
5 | ## Includes ?
6 | ---
7 |
8 | 1. Feed like web github home.
9 | 2. Watched repos' notification.
10 | 3. Trends.
11 | 4. Personal.
12 |
13 | ## How to run iOS
14 |
15 | `rm -rf node_modules`
16 |
17 | `npm install`
18 |
19 | if error about 'EACCS' try
20 |
21 | `sudo chown -R $(whoami) "$HOME/.npm"`
22 |
23 | run the project in ios dir
24 |
25 | (***注意*** 如果要contribute cocoapods 建议使用0.35.0版本!)
26 |
27 | in iOS dir
28 |
29 | `pod install`
30 |
31 | ###接入流程
32 |
33 | 1. 在index.ios.js(index.android.js)里写入Production的 deploymentKey,通常在componentDidMount里写入如下:
34 |
35 | ```js
36 | const codePush = require('react-native-code-push');
37 |
38 | codePush.sync({
39 | updateDialog: true,
40 | installMode: codePush.InstallMode.IMMEDIATE,
41 | deploymentKey: dpkey,
42 | });
43 | ```
44 | 2. 修改xcode的edit schema 使得build app是 release模式的,在appDelegate里写入:
45 | ```js
46 | NSURL *jsCodeLocation;
47 |
48 | #ifdef DEBUG
49 | jsCodeLocation = [NSURL URLWithString:@"http://30.10.111.158:8081/index.ios.bundle?platform=ios&dev=true"];
50 |
51 | jsCodeLocation = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]];
52 |
53 | #else
54 | jsCodeLocation = [CodePush bundleURL];
55 | #endif
56 | ```
57 | 3. 随意乱改js文件
58 | 4. 打包 所有js文件在一个其他位置,可以使用`打包需要更新的资源`
59 | 5. 用code-push发布。
60 |
61 | ## CodePush
62 |
63 | 注意在打包的目标地址先建立对应的目录比如这里的~/Desktop/release,要现在Desktop建立好。
64 |
65 | 打包到桌面本地资源(包括图片):
66 | react-native bundle --platform ios --entry-file index.ios.js --bundle-output ./ios/main.jsbundle --dev false
67 |
68 | 查看更新状态:
69 | code-push deployment ls GitFeed-iOS
70 |
71 | 发布更新:
72 | code-push release GitFeed-iOS ~/Desktop/release 1.0.0 -d Production
73 |
--------------------------------------------------------------------------------
/android/app/BUCK:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # To learn about Buck see [Docs](https://buckbuild.com/).
4 | # To run your application with Buck:
5 | # - install Buck
6 | # - `npm start` - to start the packager
7 | # - `cd android`
8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
10 | # - `buck install -r android/app` - compile, install and run application
11 | #
12 |
13 | lib_deps = []
14 | for jarfile in glob(['libs/*.jar']):
15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
16 | lib_deps.append(':' + name)
17 | prebuilt_jar(
18 | name = name,
19 | binary_jar = jarfile,
20 | )
21 |
22 | for aarfile in glob(['libs/*.aar']):
23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
24 | lib_deps.append(':' + name)
25 | android_prebuilt_aar(
26 | name = name,
27 | aar = aarfile,
28 | )
29 |
30 | android_library(
31 | name = 'all-libs',
32 | exported_deps = lib_deps
33 | )
34 |
35 | android_library(
36 | name = 'app-code',
37 | srcs = glob([
38 | 'src/main/java/**/*.java',
39 | ]),
40 | deps = [
41 | ':all-libs',
42 | ':build_config',
43 | ':res',
44 | ],
45 | )
46 |
47 | android_build_config(
48 | name = 'build_config',
49 | package = 'com.gitfeed',
50 | )
51 |
52 | android_resource(
53 | name = 'res',
54 | res = 'src/main/res',
55 | package = 'com.gitfeed',
56 | )
57 |
58 | android_binary(
59 | name = 'app',
60 | package_type = 'debug',
61 | manifest = 'src/main/AndroidManifest.xml',
62 | keystore = '//android/keystores:debug',
63 | deps = [
64 | ':app-code',
65 | ],
66 | )
67 |
--------------------------------------------------------------------------------
/js/common/F8StyleSheet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @providesModule F8StyleSheet
23 | * @flow
24 | */
25 |
26 | 'use strict';
27 |
28 | import { StyleSheet, Platform } from 'react-native';
29 |
30 | export function create(styles) {
31 | const platformStyles = {};
32 | Object.keys(styles).forEach((name) => {
33 | let { ios, android, ...style } = { ...styles[name] };
34 | if (ios && Platform.OS === 'ios') {
35 | style = { ...style, ...ios };
36 | }
37 | if (android && Platform.OS === 'android') {
38 | style = { ...style, ...android };
39 | }
40 | platformStyles[name] = style;
41 | });
42 | return StyleSheet.create(platformStyles);
43 | }
44 |
--------------------------------------------------------------------------------
/AppIcons/watchkit/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "24x24",
5 | "idiom" : "watch",
6 | "scale" : "2x",
7 | "filename" : "Icon-24@2x.png",
8 | "role" : "notificationCenter",
9 | "subtype" : "38mm"
10 | },
11 | {
12 | "size" : "27.5x27.5",
13 | "idiom" : "watch",
14 | "scale" : "2x",
15 | "filename" : "Icon-27.5@2x.png",
16 | "role" : "notificationCenter",
17 | "subtype" : "42mm"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "watch",
22 | "filename" : "Icon-29@2x.png",
23 | "role" : "companionSettings",
24 | "scale" : "2x"
25 | },
26 | {
27 | "size" : "29x29",
28 | "idiom" : "watch",
29 | "filename" : "Icon-29@3x.png",
30 | "role" : "companionSettings",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "watch",
36 | "scale" : "2x",
37 | "filename" : "Icon-40@2x.png",
38 | "role" : "appLauncher",
39 | "subtype" : "38mm"
40 | },
41 | {
42 | "size" : "44x44",
43 | "idiom" : "watch",
44 | "scale" : "2x",
45 | "filename" : "Icon-44@2x.png",
46 | "role" : "longLook",
47 | "subtype" : "42mm"
48 | },
49 | {
50 | "size" : "86x86",
51 | "idiom" : "watch",
52 | "scale" : "2x",
53 | "filename" : "Icon-86@2x.png",
54 | "role" : "quickLook",
55 | "subtype" : "38mm"
56 | },
57 | {
58 | "size" : "98x98",
59 | "idiom" : "watch",
60 | "scale" : "2x",
61 | "filename" : "Icon-98@2x.png",
62 | "role" : "quickLook",
63 | "subtype" : "42mm"
64 | }
65 | ],
66 | "info" : {
67 | "version" : 1,
68 | "author" : "makeappicon"
69 | }
70 | }
--------------------------------------------------------------------------------
/js/constants/Colors.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @providesModule Colors
3 | * @flow
4 | */
5 |
6 | const TINT_COLOR = '#586872'
7 |
8 | export default {
9 | tintColor: TINT_COLOR,
10 | textBlack: '#333333',
11 | inactiveText: '#9B9B9B',
12 | textGray: '#767676',
13 | textLink: '#4078C0',
14 | textDark: '#666666',
15 | textLight: '#7F91A7',
16 | cellBorder: '#E2E2E2',
17 | tabIconDefault: '#888',
18 | tabIconSelected: TINT_COLOR,
19 | red: '#f12938',
20 | green: '#80BD01',
21 | backGray: '#E5E5E5',
22 | lightGray: '#cccccc',
23 | gray: '#F0EFF4',
24 | lightBlack: '#919191',
25 | blue: '#0084FF',
26 | imagePlaceholder: '#EFEEEE',
27 | touchableHighlight: '#E5E5E5'
28 | };
29 |
30 |
31 | //const BRAND_COLOR = '#5C069A';
32 | //const VERY_LIGHT_GREY = '#f5f5f5';
33 | //const LIGHT_GREY = '#cccccc';
34 | //const GREY = '#888';
35 | //const MID_GREY = '#444';
36 | //const DARK_GREY = '#222';
37 | //
38 | //export default {
39 | // navigationBarTintColor: '#fff',
40 | // navigationBarBackgroundColor: BRAND_COLOR,
41 | // tabIconDefault: GREY,
42 | // tabIconSelected: BRAND_COLOR,
43 | // tabBar: '#fefefe',
44 | // drawerIconDefault: GREY,
45 | // drawerTextDefault: MID_GREY,
46 | // drawerIconSelected: BRAND_COLOR,
47 | //
48 | // veryLightGrey: VERY_LIGHT_GREY,
49 | // lightGrey: LIGHT_GREY,
50 | // grey: GREY,
51 | // midGrey: MID_GREY,
52 | // darkGrey: DARK_GREY,
53 | // tintColor: BRAND_COLOR,
54 | // lineGray: '#F0F0F0',
55 | // green: '#80BD01',
56 | // backGray: '#E5E5E5',
57 | // textGray: '#9A9A9A',
58 | // textBlack: '#333333',
59 | // purple: '#9966CC',
60 | // red: '#f12938',
61 | // backWhite: '#F2F2F2',
62 | // textGold: '#BC7233',
63 | // borderColor: '#E2E2E2',
64 | // black: '#586872',
65 | // blue: '#4078c0',
66 | // theme: '#586872',
67 | // lightBlack: '#919191',
68 | // gray: '#F0EFF4'
69 | //};
70 |
--------------------------------------------------------------------------------
/js/store/track.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @flow
23 | */
24 |
25 | 'use strict'
26 |
27 | import { NativeModules } from 'react-native'
28 | const AppEventsLogger = NativeModules.AppEventsLogger
29 |
30 | function track(action) {
31 | switch (action.type) {
32 | // case 'FAV_LOOK':
33 | // AppEventsLogger.logEvent(action.type, { title: action.title })
34 | // break
35 | // case 'SEARCH_START':
36 | // AppEventsLogger.logEvent(action.type, { ...action })
37 | // break
38 | // case 'SHARE_LOOK':
39 | // AppEventsLogger.logEvent(action.type, {
40 | // url: action.imgObj.url,
41 | // scene: JSON.stringify(action.imgObj.scene)
42 | // })
43 | // break
44 | }
45 | }
46 |
47 | module.exports = track;
48 |
--------------------------------------------------------------------------------
/js/common/F8Touchable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @providesModule F8Touchable
23 | * @flow
24 | */
25 |
26 | 'use strict';
27 |
28 | import React from 'react';
29 | import {
30 | TouchableHighlight,
31 | TouchableNativeFeedback,
32 | Platform,
33 | } from 'react-native';
34 | import Colors from '../constants/Colors'
35 |
36 | function F8TouchableIOS(props) {
37 | return (
38 |
43 | );
44 | }
45 |
46 | // TODO 当用 TouchableNativeFeedback wrap Image 的时候,图片显示不出来
47 | const F8Touchable = Platform.OS === 'android'
48 | ? TouchableNativeFeedback
49 | : F8TouchableIOS;
50 |
51 | module.exports = F8Touchable;
52 |
--------------------------------------------------------------------------------
/js/reducers/language.js:
--------------------------------------------------------------------------------
1 | import * as lg from '../actions/language'
2 |
3 | export const defaultState = {
4 | allLanguages: [
5 | "All Languages",
6 | "ActionScript",
7 | "C",
8 | "C#",
9 | "C++",
10 | "Clojure",
11 | "CoffeeScript",
12 | "CSS",
13 | "Go",
14 | "Haskell",
15 | "HTML",
16 | "Java",
17 | "JavaScript",
18 | "Lua",
19 | "Matlab",
20 | "Objective-C",
21 | "Objective-C++",
22 | "Perl",
23 | "PHP",
24 | "Python",
25 | "R",
26 | "Ruby",
27 | "Scala",
28 | "Shell",
29 | "Swift",
30 | "TeX",
31 | "VimL"
32 | ],
33 | trendingLanguages: [
34 | "All Languages",
35 | "C#",
36 | "C++",
37 | "Go",
38 | "Java",
39 | "JavaScript",
40 | "Objective-C",
41 | "PHP",
42 | "Python",
43 | "Ruby",
44 | "Scala",
45 | "Shell",
46 | "Swift",
47 | ],
48 | rules: [
49 | "Best matches",
50 | "Most stars",
51 | "Most forks",
52 | "Recent updated"
53 | ],
54 | currentLanguage: "All Languages",
55 | currentRule: "Best matches"
56 | }
57 |
58 | export const language = (state = defaultState, action) => {
59 | switch (action.type) {
60 | case lg.CHANGE_LANGUAGE:
61 | return { ...state, currentLanguage: action.currentLanguage }
62 | case lg.CHANGE_RULE:
63 | return { ...state, currentRule: action.currentRule }
64 | }
65 |
66 | return state
67 | }
68 |
69 | export const mapRuleToQuery = rule => {
70 | let res = ''
71 | switch (rule) {
72 | case 'Best matches':
73 | res = ''
74 | break
75 | case 'Most stars':
76 | res = 'stars'
77 | break
78 | case 'Most forks':
79 | res = 'forks'
80 | break
81 | case 'Recent updated':
82 | res = 'updated'
83 | break
84 | }
85 |
86 | if (!res.length) return res
87 | return `+sort:${res}`
88 | }
--------------------------------------------------------------------------------
/js/store/promise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | */
22 |
23 | 'use strict';
24 |
25 | function warn(error) {
26 | console.warn(error.message || error);
27 | throw error; // To let the caller handle the rejection
28 | }
29 |
30 | module.exports = store => next => action =>
31 | typeof action.then === 'function'
32 | ? Promise.resolve(action).then(next, warn)
33 | : next(action);
34 |
35 |
36 | // Warning: Naïve implementation!
37 | // That's *not* Redux API.
38 | /*
39 | function applyMiddleware(store, middlewares) {
40 | middlewares = middlewares.slice()
41 | middlewares.reverse()
42 |
43 | let dispatch = store.dispatch
44 | middlewares.forEach(middleware =>
45 | dispatch = middleware(store)(dispatch)
46 | )
47 |
48 | return Object.assign({}, store, { dispatch })
49 | }
50 | */
--------------------------------------------------------------------------------
/AppIcons/ios/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x",
7 | "filename" : "Icon-Small@2x.png"
8 | },
9 | {
10 | "idiom" : "iphone",
11 | "size" : "29x29",
12 | "scale" : "3x",
13 | "filename" : "Icon-Small@3x.png"
14 | },
15 | {
16 | "idiom" : "iphone",
17 | "size" : "40x40",
18 | "scale" : "2x",
19 | "filename" : "Icon-40@2x.png"
20 | },
21 | {
22 | "idiom" : "iphone",
23 | "size" : "40x40",
24 | "scale" : "3x",
25 | "filename" : "Icon-40@3x.png"
26 | },
27 | {
28 | "idiom" : "iphone",
29 | "size" : "60x60",
30 | "scale" : "2x",
31 | "filename" : "Icon-60@2x.png"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "3x",
37 | "filename" : "Icon-60@3x.png"
38 | },
39 | {
40 | "idiom" : "ipad",
41 | "size" : "29x29",
42 | "scale" : "1x",
43 | "filename" : "Icon-Small.png"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "size" : "29x29",
48 | "scale" : "2x",
49 | "filename" : "Icon-Small@2x.png"
50 | },
51 | {
52 | "idiom" : "ipad",
53 | "size" : "40x40",
54 | "scale" : "1x",
55 | "filename" : "Icon-40.png"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "40x40",
60 | "scale" : "2x",
61 | "filename" : "Icon-40@2x.png"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "76x76",
66 | "scale" : "1x",
67 | "filename" : "Icon-76.png"
68 | },
69 | {
70 | "idiom" : "ipad",
71 | "size" : "76x76",
72 | "scale" : "2x",
73 | "filename" : "Icon-76@2x.png"
74 | }
75 | ],
76 | "info" : {
77 | "version" : 1,
78 | "author" : "makeappicon"
79 | }
80 | }
--------------------------------------------------------------------------------
/ios/gitfeed/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 | #import "CodePush.h"
12 | #import "MobClick.h"
13 | #import "RCTRootView.h"
14 |
15 | @implementation AppDelegate
16 |
17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
18 | {
19 | UMConfigInstance.appKey = @"56a8db12e0f55a3cd40005f2";
20 | [MobClick startWithConfigure:UMConfigInstance];
21 |
22 | NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024 diskCapacity:100 * 1024 * 1024 diskPath:nil];
23 | [NSURLCache setSharedURLCache:urlCache];
24 |
25 | NSURL *jsCodeLocation;
26 |
27 | #ifdef DEBUG
28 | jsCodeLocation = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]];
29 | #else
30 | jsCodeLocation = [CodePush bundleURL];
31 | #endif
32 |
33 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
34 | moduleName:@"gitfeed"
35 | initialProperties:nil
36 | launchOptions:launchOptions];
37 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
38 |
39 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
40 | UIViewController *rootViewController = [UIViewController new];
41 | rootViewController.view = rootView;
42 | self.window.rootViewController = rootViewController;
43 | [self.window makeKeyAndVisible];
44 | return YES;
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/js/actions/parse.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @flow
23 | */
24 |
25 | 'use strict';
26 |
27 | const Parse = require('parse/react-native');
28 | const logError = require('logError');
29 | const InteractionManager = require('InteractionManager');
30 | const Notification = Parse.Object.extend('Notification');
31 |
32 | function loadParseQuery(type, query) {
33 | return (dispatch) => {
34 | return query.find({
35 | success: (list) => {
36 | // We don't want data loading to interfere with smooth animations
37 | InteractionManager.runAfterInteractions(() => {
38 | // Flow can't guarantee {type, list} is a valid action
39 | dispatch(({type, list}: any));
40 | });
41 | },
42 | error: logError,
43 | });
44 | };
45 | }
46 |
47 | export const loadNotifications = () => loadParseQuery('LOADED_NOTIFICATIONS',
48 | new Parse.Query(Notification))
49 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | # We fork some components by platform.
4 | .*/*[.]android.js
5 |
6 | # Ignore templates with `@flow` in header
7 | .*/local-cli/generator.*
8 |
9 | # Ignore malformed json
10 | .*/node_modules/y18n/test/.*\.json
11 |
12 | # Ignore the website subdir
13 | /website/.*
14 |
15 | # Ignore BUCK generated dirs
16 | /\.buckd/
17 |
18 | # Ignore unexpected extra @providesModule
19 | .*/node_modules/commoner/test/source/widget/share.js
20 | .*/node_modules/.*/node_modules/fbjs/.*
21 |
22 | # Ignore duplicate module providers
23 | # For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root
24 | .*/Libraries/react-native/React.js
25 | .*/Libraries/react-native/ReactNative.js
26 | .*/node_modules/jest-runtime/build/__tests__/.*
27 |
28 | [include]
29 |
30 | [libs]
31 | node_modules/react-native/Libraries/react-native/react-native-interface.js
32 | node_modules/react-native/flow
33 | flow/
34 |
35 | [options]
36 | module.system=haste
37 |
38 | esproposal.class_static_fields=enable
39 | esproposal.class_instance_fields=enable
40 |
41 | experimental.strict_type_args=true
42 |
43 | munge_underscores=true
44 |
45 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
46 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
47 |
48 | suppress_type=$FlowIssue
49 | suppress_type=$FlowFixMe
50 | suppress_type=$FixMe
51 |
52 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
53 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-3]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
54 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
55 |
56 | unsafe.enable_getters_and_setters=true
57 |
58 | [version]
59 | ^0.33.0
60 |
--------------------------------------------------------------------------------
/ios/Classes/NavigationCategory/DXTextCategoryMenu.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXTextCategoryMenu.h
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/10/23.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class DXTextCategoryMenu;
12 |
13 | @protocol DXTextCategoryMenuDelegate
14 |
15 | @optional
16 | - (void)textCategoryMenu:(DXTextCategoryMenu *)menu willSelectedMenuFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)index;
17 |
18 | - (void)textCategoryMenu:(DXTextCategoryMenu *)menu didSelectedMenuFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)index;
19 |
20 | @end
21 |
22 | @interface DXTextCategoryMenu : UIView
23 |
24 | @property (nonatomic, weak) id delegate;
25 | @property (nonatomic, strong) NSArray *options;
26 |
27 | // If the first item stays the position when others scroll
28 | @property (nonatomic, assign) BOOL lockStartPosition;
29 |
30 | // Selected color, default is red
31 | @property (nonatomic, strong) UIColor *selectedColor;
32 |
33 | // UnSelected color, default is lightgray
34 | @property (nonatomic, strong) UIColor *unSelectedColor;
35 |
36 | // The menu contentInset in container. default is {2, 5, 2, 5}
37 | @property (nonatomic, assign) UIEdgeInsets contentInset;
38 |
39 | // Each text menu between space
40 | @property (nonatomic, assign) CGFloat spacingBetweenMenu;
41 |
42 | // The bottom line height, default is 2
43 | @property (nonatomic, assign) CGFloat bottomLineHeight;
44 |
45 | // The bottom line animation flys default is 0.25
46 | @property (nonatomic, assign) CGFloat selectedAnimationDuration;
47 |
48 | // The trigger scrollView autoscroll space between the side, default is 100
49 | @property (nonatomic, assign) CGFloat needCenterMenuOffset;
50 |
51 | // If use the system blur, default is YES
52 | @property (nonatomic, assign, getter=isBlur) BOOL blur;
53 |
54 | // If blur which style to use, defaut is extra light
55 | @property (nonatomic, assign) UIBlurEffectStyle blurEffectStyle;
56 |
57 | - (void)updateSelectedIndex:(NSInteger)index;
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/js/WLApp.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | AppState,
10 | View,
11 | StatusBar,
12 | ActivityIndicator
13 | } from 'react-native'
14 | import CodePush from 'react-native-code-push'
15 | import { connect } from 'react-redux'
16 | import RootNavigation from './navigation/RootNavigation'
17 | import { create } from './common/F8StyleSheet'
18 | import { getUser, login } from './actions/login'
19 | import { version } from './env'
20 | import OnboardPage from './pages/OnboardPage'
21 | //import PushNotificationsController from './PushNotificationsController'
22 |
23 | class WLApp extends Component {
24 | componentDidMount() {
25 | // TODO: 清理本地所有数据
26 | // storage.clearAll()
27 |
28 | const { dispatch } = this.props
29 | dispatch(getUser())
30 |
31 | AppState.addEventListener('change', this.handleAppStateChange)
32 | }
33 |
34 | componentWillUnmount() {
35 | AppState.removeEventListener('change', this.handleAppStateChange)
36 | }
37 |
38 | handleAppStateChange(appState) {
39 | if (appState === 'active' && !__DEV__) {
40 | CodePush.sync({ installMode: CodePush.InstallMode.ON_NEXT_RESUME })
41 | }
42 | }
43 |
44 | render() {
45 | const { isOnboard, loading } = this.props
46 |
47 | if (loading) {
48 | // TODO: A more beautiful loading like weibo avatar page should be
49 | return
50 |
51 |
52 | }
53 |
54 | return isOnboard ? :
55 | }
56 | }
57 |
58 | const styles = create({
59 | loadingContainer: {
60 | flex: 1,
61 | flexDirection: 'row',
62 | justifyContent: 'center',
63 | alignItems: 'center'
64 | },
65 |
66 | container: {
67 | flex: 1,
68 | },
69 | })
70 |
71 | const select = ({ login }) => {
72 | const { user } = login
73 | return {
74 | user,
75 | isOnboard: !!user.login && !!user.login.length,
76 | loading: login.loading
77 | }
78 | }
79 |
80 | export default connect(select)(WLApp)
--------------------------------------------------------------------------------
/js/pages/DetailPage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | Text,
10 | View,
11 | Platform
12 | } from 'react-native'
13 | import { addRecent } from '../actions/recent'
14 | import { connect } from 'react-redux'
15 |
16 | import Color from '../common/Colors'
17 | import GridView from '../common/GridView'
18 | import PicCell from '../common/PicCell'
19 | import Dimensions from 'Dimensions'
20 |
21 | const SCREENWIDTH = Dimensions.get('window').width
22 | const PICS_PER_ROW = 3
23 | const PICCELL_WIDTH = SCREENWIDTH / 3
24 | const NAV_HEIGHT = Platform.OS === 'android' ? 44 : 64
25 |
26 | class DetailPage extends Component {
27 | componentDidMount() {
28 | const { dispatch, context } = this.props
29 | dispatch(addRecent(context))
30 | }
31 |
32 | render() {
33 | return (
34 |
40 | )
41 | }
42 |
43 | _renderItem(item, index, rowID) {
44 | const key = index + PICS_PER_ROW * rowID
45 | return (
46 | )
53 | }
54 |
55 | _onSelect(item, key) {
56 | this.props.navigator.push({
57 | id: 'ShareLookPage',
58 | title: this.props.context.title || '天天表情',
59 | context: {
60 | detail: this.props.context,
61 | selectIndex: key
62 | }
63 | })
64 | }
65 | }
66 |
67 | DetailPage.propTypes = {
68 | context: PropTypes.object
69 | }
70 | DetailPage.defaultProps = {}
71 |
72 | export default connect()(DetailPage)
73 |
74 | const styles = StyleSheet.create({
75 | gridView: {
76 | marginTop: NAV_HEIGHT,
77 | backgroundColor: Color.gridViewBack
78 | },
79 | loadingView: {
80 | justifyContent: 'center',
81 | alignItems: 'center'
82 | }
83 | })
84 |
--------------------------------------------------------------------------------
/js/common/GridPage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | Text,
10 | View,
11 | } from 'react-native'
12 |
13 | import Color from '../common/Colors'
14 | import GridView from '../common/GridView'
15 | import PicCell from '../common/PicCell'
16 | import Dimensions from 'Dimensions'
17 |
18 | const reactMixin = require('react-mixin')
19 | const TimerMixin = require('react-timer-mixin')
20 |
21 | const SCREEN_WIDTH = Dimensions.get('window').width
22 | const PICS_PER_ROW = 3
23 | const CELL_SIZE = SCREEN_WIDTH / 3
24 |
25 | class GridPage extends Component {
26 | constructor(props) {
27 | super(props)
28 | }
29 |
30 | render() {
31 | return (
32 |
40 | )
41 | }
42 |
43 | _renderItem(item, index, rowID) {
44 | const displayIndex = item.displayIndex || 0
45 | return (
46 |
53 | )
54 | }
55 |
56 | _onSelect(detail) {
57 | this.requestAnimationFrame(() => {
58 | this.props.navigator.push({
59 | id: 'ShareLookPage',
60 | title: detail.title,
61 | context: {
62 | detail,
63 | selectIndex: detail.displayIndex || 0
64 | }
65 | })
66 | })
67 | }
68 | }
69 |
70 | GridPage.propTypes = {
71 | dataSource: PropTypes.array.isRequired
72 | }
73 |
74 | GridPage.defaultProps = {
75 | dataSource: []
76 | }
77 |
78 | reactMixin(GridPage.prototype, TimerMixin)
79 |
80 | export default GridPage
81 |
82 | const styles = StyleSheet.create({
83 | gridView: {
84 | backgroundColor: Color.gridViewBack
85 | },
86 | loadingView: {
87 | justifyContent: 'center',
88 | alignItems: 'center'
89 | }
90 | })
91 |
--------------------------------------------------------------------------------
/js/pages/HotTags.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | View,
10 | Text,
11 | TouchableOpacity,
12 | Dimensions
13 | } from 'react-native'
14 | import { connect } from 'react-redux'
15 | import { getHotTags, startSearch } from '../actions/search'
16 | import CStyles from '../constants/Styles'
17 | import Color from '../constants/Colors'
18 |
19 | const RoundTagButton = require('../common/RoundTagButton')
20 |
21 | class HotTags extends Component {
22 | componentDidMount() {
23 | this.props.dispatch && this.props.dispatch(getHotTags())
24 | }
25 |
26 | render() {
27 | let { tags, style, dispatch } = this.props
28 | tags = tags && tags.map((tag, i) => {
29 | const color = i === 0 ? Color.blue : Color.lightBlack
30 | return (
31 | dispatch(startSearch(tag, 'TAG'))}
37 | />
38 | )
39 | })
40 | return (
41 |
42 | Suggested:
43 |
44 | {tags}
45 |
46 |
47 |
48 | )
49 | }
50 | }
51 |
52 | HotTags.propTypes = {
53 | tags: PropTypes.array
54 | }
55 | HotTags.defaultProps = {}
56 |
57 | const mapStateToProps = state => {
58 | return {
59 | tags: state.search.hotTags
60 | }
61 | }
62 |
63 | exports.HotTags = connect(mapStateToProps)(HotTags)
64 | exports.__cards__ = (define) => {
65 | define('normal', _ => )
66 | }
67 |
68 | const styles = StyleSheet.create({
69 | container: {
70 | flexDirection: 'column',
71 | },
72 | hotSearch: {
73 | margin: 5,
74 | marginBottom: 10,
75 | color: Color.lightBlack
76 | },
77 | tagContainer: {
78 | flexDirection: 'row',
79 | flexWrap: 'wrap',
80 | padding: 3
81 | },
82 | tag: {
83 | marginLeft: 10,
84 | marginBottom: 10,
85 | borderColor: Color.lightBlack
86 | },
87 | })
--------------------------------------------------------------------------------
/js/common/F8Colors.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @providesModule F8Colors
23 | * @flow
24 | */
25 |
26 | 'use strict';
27 |
28 | const LOCATION_COLORS = {
29 | 'HERBST': '#00E3AD',
30 | 'HERBST A': '#00E3AD',
31 | 'HERBST B': '#00E3AD',
32 | 'HACKER X': '#4D99EF',
33 | 'HACKER Y': '#CF72B1',
34 | 'COWELL': '#6A6AD5',
35 | 'COWELL C': '#6A6AD5',
36 | 'FOOD TENT': '#FFCD3B',
37 | };
38 |
39 | function colorForLocation(location: ?string): string {
40 | if (!location) {
41 | return 'black';
42 | }
43 |
44 | var color = LOCATION_COLORS[location.toUpperCase()];
45 | if (!color) {
46 | console.warn(`Location '${location}' has no color`);
47 | color = 'black';
48 | }
49 | return color;
50 | }
51 |
52 | function colorForTopic(count: number, index: number): string {
53 | const hue = Math.round(360 * index / (count + 1));
54 | return `hsl(${hue}, 74%, 65%)`;
55 | }
56 |
57 | module.exports = {
58 | actionText: '#3FB4CF',
59 | inactiveText: '#9B9B9B',
60 | darkText: '#032250',
61 | lightText: '#7F91A7',
62 | cellBorder: '#EEEEEE',
63 | darkBackground: '#183E63',
64 | colorForLocation,
65 | colorForTopic,
66 | };
67 |
--------------------------------------------------------------------------------
/ios/Classes/UIView+TopBarMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TopBarMessage.h
3 | // DXTopBarMessageView
4 | //
5 | // Created by xiekw on 14-3-17.
6 | // Copyright (c) 2014年 xiekw. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TopWarningView : UIView
12 |
13 | @property (nonatomic, strong) NSString *warningText;
14 | @property (nonatomic, strong) UIImageView *iconIgv;
15 | @property (nonatomic, copy) dispatch_block_t tapHandler;
16 | @property (nonatomic, assign) CGFloat dimissDelay;
17 | @property (nonatomic, strong) UILabel *label;
18 |
19 | - (void)resetViews;
20 |
21 | @end
22 |
23 | @interface UIView (TopBarMessage)
24 |
25 | /**
26 | * Set the global default apperance of the top message, may be the appdelegate class is a good place to setup
27 | *
28 | * @param apperance the top bar view config, the whole version will be @{kDXTopBarBackgroundColor:[UIColor blueColor], kDXTopBarTextColor : [UIColor yellowColor], kDXTopBarIcon : [UIImage imageNamed:@"icon.png"], kDXTopBarTextFont : [UIFont boldSystemFontOfSize:15.0]}
29 | */
30 | + (void)setTopMessageDefaultApperance:(NSDictionary *)apperance;
31 |
32 | /**
33 | * show the message with config on the top bar, note the config won't change the default top message apperance, this is the setTopMessageDefaultApperance: does.
34 | *
35 | * @param message the text to show
36 | * @param config the top bar view config, the whole version will be @{kDXTopBarBackgroundColor:[UIColor blueColor], kDXTopBarTextColor : [UIColor yellowColor], kDXTopBarIcon : [UIImage imageNamed:@"icon.png"], kDXTopBarTextFont : [UIFont boldSystemFontOfSize:15.0]}
37 | * @param delay time to dismiss
38 | * @param tapHandler the tap handler
39 | */
40 | - (void)showTopMessage:(NSString *)message topBarConfig:(NSDictionary *)config dismissDelay:(CGFloat)delay withTapBlock:(dispatch_block_t)tapHandler;
41 |
42 | /**
43 | * Default style message something like Instagram does
44 | *
45 | * @param message message to tell
46 | */
47 | - (void)showTopMessage:(NSString *)message;
48 |
49 | @end
50 |
51 | extern NSString * const kDXTopBarBackgroundColor;
52 | extern NSString * const kDXTopBarTextColor;
53 | extern NSString * const kDXTopBarTextFont;
54 | extern NSString * const kDXTopBarIcon;
55 | extern NSString * const kDXTopBarOffset;
56 |
--------------------------------------------------------------------------------
/ios/gitfeed/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | GitFeed
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 | 2.0.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 100
25 | CodePushDeploymentKey
26 | deployment-key-here
27 | LSRequiresIPhoneOS
28 |
29 | NSAppTransportSecurity
30 |
31 | NSAllowsArbitraryLoads
32 |
33 | NSExceptionDomains
34 |
35 | localhost
36 |
37 | NSExceptionAllowsInsecureHTTPLoads
38 |
39 |
40 |
41 |
42 | NSLocationWhenInUseUsageDescription
43 |
44 | UIAppFonts
45 |
46 | Entypo.ttf
47 | EvilIcons.ttf
48 | FontAwesome.ttf
49 | Foundation.ttf
50 | Ionicons.ttf
51 | MaterialIcons.ttf
52 | Octicons.ttf
53 | SimpleLineIcons.ttf
54 | Zocial.ttf
55 |
56 | UILaunchStoryboardName
57 | LaunchScreen
58 | UIRequiredDeviceCapabilities
59 |
60 | armv7
61 |
62 | UISupportedInterfaceOrientations
63 |
64 | UIInterfaceOrientationPortrait
65 | UIInterfaceOrientationLandscapeLeft
66 | UIInterfaceOrientationLandscapeRight
67 |
68 | UIViewControllerBasedStatusBarAppearance
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/js/PushNotificationsController.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | Platform,
9 | View,
10 | AppState,
11 | PushNotificationIOS
12 | } from 'react-native'
13 | import { connect } from 'react-redux'
14 | import PushNotification from 'react-native-push-notification'
15 | import {
16 | storeDeviceToken,
17 | receivePushNotification,
18 | updateInstallation,
19 | checkPushNotifications,
20 | loadNotifications
21 | } from './actions'
22 |
23 | class AppBadgeController extends React.Component {
24 | constructor() {
25 | super()
26 | this.handleAppStateChange = this.handleAppStateChange.bind(this)
27 | }
28 |
29 | handleAppStateChange(appState) {
30 | if (appState === 'active') {
31 | this.updateAppBadge()
32 | this.props.dispatch(loadNotifications())
33 | }
34 | }
35 |
36 | componentDidMount() {
37 | AppState.addEventListener('change', this.handleAppStateChange)
38 |
39 | const { dispatch } = this.props
40 | dispatch(checkPushNotifications())
41 | dispatch(loadNotifications())
42 |
43 | PushNotification.configure({
44 | onRegister: ({ token }) => {
45 | dispatch(storeDeviceToken(token))
46 | dispatch(checkPushNotifications())
47 | },
48 | onNotification: notif => dispatch(receivePushNotification(notif)),
49 | permissions: {
50 | alert: true,
51 | badge: true,
52 | sound: true
53 | },
54 | requestPermissions: this.props.enabled,
55 | })
56 |
57 | this.updateAppBadge()
58 | }
59 |
60 | componentWillUnmount() {
61 | AppState.removeEventListener('change', this.handleAppStateChange)
62 | }
63 |
64 | componentDidUpdate(prevProps) {
65 | if (!prevProps.enabled && this.props.enabled) {
66 | PushNotification.requestPermissions()
67 | }
68 | }
69 |
70 | updateAppBadge() {
71 | if (Platform.OS === 'ios') {
72 | PushNotificationIOS.setApplicationIconBadgeNumber(0)
73 | updateInstallation({ badge: 0 })
74 | }
75 | }
76 |
77 | render() {
78 | return null
79 | }
80 | }
81 |
82 | function select(store) {
83 | return {
84 | enabled: store.notifications.enabled === true,
85 | }
86 | }
87 |
88 | module.exports = connect(select)(AppBadgeController)
89 |
--------------------------------------------------------------------------------
/ios/gitfeedTests/gitfeedTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | #import "RCTLog.h"
14 | #import "RCTRootView.h"
15 |
16 | #define TIMEOUT_SECONDS 600
17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
18 |
19 | @interface gitfeedTests : XCTestCase
20 |
21 | @end
22 |
23 | @implementation gitfeedTests
24 |
25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
26 | {
27 | if (test(view)) {
28 | return YES;
29 | }
30 | for (UIView *subview in [view subviews]) {
31 | if ([self findSubviewInView:subview matching:test]) {
32 | return YES;
33 | }
34 | }
35 | return NO;
36 | }
37 |
38 | - (void)testRendersWelcomeScreen
39 | {
40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
42 | BOOL foundElement = NO;
43 |
44 | __block NSString *redboxError = nil;
45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
46 | if (level >= RCTLogLevelError) {
47 | redboxError = message;
48 | }
49 | });
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | RCTSetLogFunction(RCTDefaultLogFunction);
64 |
65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
67 | }
68 |
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 | *.sql
27 | *.sqlite
28 |
29 | # OS generated files #
30 | ######################
31 | .DS_Store
32 | .DS_Store?
33 | ._*
34 | .Spotlight-V100
35 | .Trashes
36 | ehthumbs.db
37 | Thumbs.db
38 |
39 | # Xcode
40 | #
41 | #build/
42 | *.pbxuser
43 | !default.pbxuser
44 | *.mode1v3
45 | !default.mode1v3
46 | *.mode2v3
47 | !default.mode2v3
48 | *.perspectivev3
49 | !default.perspectivev3
50 | xcuserdata
51 | *.xccheckout
52 | *.moved-aside
53 | DerivedData
54 | *.hmap
55 | *.xcuserstate
56 |
57 | # CocoaPods
58 | #
59 | # We recommend against adding the Pods directory to your .gitignore. However
60 | # you should judge for yourself, the pros and cons are mentioned at:
61 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
62 | #
63 | Pods/
64 |
65 | # Nodejs
66 | #
67 |
68 | # Logs
69 | logs
70 | *.log
71 | npm-debug.log*
72 |
73 | # Runtime data
74 | pids
75 | *.pid
76 | *.seed
77 |
78 | # Directory for instrumented libs generated by jscoverage/JSCover
79 | lib-cov
80 |
81 | # Coverage directory used by tools like istanbul
82 | coverage
83 |
84 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
85 | .grunt
86 |
87 | # node-waf configuration
88 | .lock-wscript
89 |
90 | # Compiled binary addons (http://nodejs.org/api/addons.html)
91 | #build/Release
92 |
93 | # Dependency directory
94 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
95 | node_modules
96 |
97 |
98 | # tree
99 | tree_out
100 |
101 | # Android
102 | android/.gradle/
103 | android/.idea/
104 | android/app/build
105 | android/app.iml
106 | android/gradle
107 | android/build
108 | android/*/build
109 | local.properties
110 | *.keystore
111 |
112 |
113 | # BUCK
114 | buck-out/
115 | \.buckd/
116 | android/app/libs
117 |
118 | # iOS
119 | ios/main.jsbundle
120 | ios/main.jsbundle.meta
--------------------------------------------------------------------------------
/js/reducers/notifications.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Facebook, Inc.
3 | *
4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to
5 | * use, copy, modify, and distribute this software in source code or binary
6 | * form for use in connection with the web services and APIs provided by
7 | * Facebook.
8 | *
9 | * As with any software that integrates with the Facebook platform, your use
10 | * of this software is subject to the Facebook Developer Principles and
11 | * Policies [http://developers.facebook.com/policy/]. This copyright notice
12 | * shall be included in all copies or substantial portions of the software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE
21 | *
22 | * @flow
23 | */
24 |
25 | 'use strict';
26 |
27 | var Platform = require('Platform');
28 |
29 | const initialState = {
30 | enabled: Platform.OS === 'ios' ? null : true,
31 | registered: false,
32 | disabled: false,
33 | server: []
34 | };
35 |
36 | export const notifications = (state = initialState, action) => {
37 | switch (action.type) {
38 | case 'LOADED_NOTIFICATIONS':
39 | let list = action.list.map(fromParseObject);
40 | return {...state, server: list};
41 |
42 | case 'TURNED_ON_PUSH_NOTIFICATIONS':
43 | return { ...state, enabled: true };
44 |
45 | case 'SKIPPED_PUSH_NOTIFICATIONS':
46 | return { ...state, enabled: false };
47 |
48 | case 'REGISTERED_PUSH_NOTIFICATIONS':
49 | return { ...state, registered: true };
50 |
51 | case 'RESET_NUXES':
52 | return { ...state, enabled: initialState.enabled };
53 |
54 | case 'CHECK_PUSH_NOTIFICATIONS':
55 | return { ...state, disabled: action.disabled }
56 |
57 | default:
58 | return state;
59 | }
60 | }
61 |
62 | const fromParseObject = object => {
63 | return {
64 | id: object.id,
65 | text: object.get('text'),
66 | url: object.get('url'),
67 | time: object.createdAt.getTime(),
68 | }
69 | }
--------------------------------------------------------------------------------
/js/common/IconCell.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import React, {
4 | Component,
5 | PropTypes,
6 | } from 'react'
7 | import {
8 | StyleSheet,
9 | View,
10 | Text,
11 | TouchableOpacity,
12 | } from 'react-native'
13 | import Color from '../constants/Colors'
14 | import Icon from 'react-native-vector-icons/Ionicons'
15 | import CStyles from '../constants/Styles'
16 |
17 | class IconCell extends Component {
18 | render() {
19 | const { text, leftIcon, rightIcon, onPressRightIcon, onPress } = this.props
20 | let rightButton
21 | if (rightIcon) {
22 | rightButton = (
23 |
26 |
30 |
31 | )
32 | }
33 | return (
34 |
35 |
38 |
42 |
43 | {text}
44 |
45 | {rightButton}
46 |
47 |
48 |
49 | )
50 | }
51 | }
52 |
53 | IconCell.propTypes = {
54 | text: PropTypes.string.isRequired,
55 | leftIcon: PropTypes.string,
56 | rightIcon: PropTypes.string,
57 | onPressRightIcon: PropTypes.func,
58 | onPress: PropTypes.func
59 | }
60 | IconCell.defaultProps = {
61 | text: '使用 Icon cell',
62 | leftIcon: 'ios-time-outline',
63 | }
64 |
65 | module.exports.__cards__ = (define) => {
66 | define('normal', _ => )
67 | define('right', _ => )
68 | }
69 |
70 | export default IconCell
71 |
72 | const styles = StyleSheet.create({
73 | container: {
74 | flexDirection: 'row',
75 | alignItems: 'center',
76 | height: 40,
77 | backgroundColor: 'white'
78 | },
79 | text: {
80 | fontSize: 15,
81 | color: Color.black
82 | },
83 | rightIconButton: {
84 | width: 40,
85 | height: 40,
86 | position: 'absolute',
87 | right: 0,
88 | top: 0,
89 | flexDirection: 'row',
90 | alignItems: 'center',
91 | justifyContent: 'center'
92 | }
93 | })
--------------------------------------------------------------------------------
/ios/Classes/DXTopMessageManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // DXTopMessageManager.m
3 | // RN_CNNode
4 | //
5 | // Created by xiekw on 15/10/21.
6 | // Copyright © 2015年 Facebook. All rights reserved.
7 | //
8 |
9 | #import "DXTopMessageManager.h"
10 | #import "RCTBridge.h"
11 | #import "RCTConvert.h"
12 | #import "RCTScrollView.h"
13 | #import "RCTUIManager.h"
14 | #import "RCTEventDispatcher.h"
15 | #import "UIView+react.h"
16 | #import "UIView+TopBarMessage.h"
17 |
18 | @implementation DXTopMessageManager
19 |
20 | @synthesize bridge = _bridge;
21 |
22 | - (dispatch_queue_t)methodQueue {
23 | return _bridge.uiManager.methodQueue;
24 | }
25 |
26 | RCT_EXPORT_MODULE();
27 |
28 | /**
29 | * Show top message
30 | *
31 | * @param config {
32 | * background: '#111111',
33 | * textColor: '#111111',
34 | * font: {'fontFamily': 'hev', 'fontSize': 12, 'fontWeight': 11, 'fontStyle': 'bold'},
35 | * icon: 'imagename',
36 | * offset: 64,
37 | * }
38 | *
39 | */
40 |
41 | RCT_EXPORT_METHOD(showTopMessage:(nonnull NSNumber *)reactTag message:(nonnull NSString *)message config:(NSDictionary *)config callback:(RCTResponseSenderBlock)callback) {
42 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
43 | dispatch_async(dispatch_get_main_queue(), ^{
44 | UIView *view = viewRegistry[reactTag];
45 | if (!view) {
46 | NSLog(@"Cannot find view with tag #%@", reactTag);
47 | return;
48 | }
49 |
50 | [view showTopMessage:message topBarConfig:[self mapTopBarConfig:config] dismissDelay:3.0 withTapBlock:^{
51 | [_bridge.eventDispatcher sendDeviceEventWithName:@"messageTapped" body:reactTag];
52 | }];
53 |
54 | callback(@[[NSNull null], reactTag]);
55 | });
56 | }];
57 |
58 | }
59 |
60 | - (NSDictionary *)mapTopBarConfig:(NSDictionary *)jsConfig {
61 | NSMutableDictionary *mdic = [NSMutableDictionary dictionaryWithCapacity:jsConfig.count];
62 | id backColor = jsConfig[@"background"];
63 | if (backColor) {
64 | mdic[kDXTopBarBackgroundColor] = [RCTConvert UIColor:backColor];
65 | }
66 |
67 | id textColor = jsConfig[@"textColor"];
68 | if (textColor) {
69 | mdic[kDXTopBarTextColor] = [RCTConvert UIColor:textColor];
70 | }
71 |
72 | id font = jsConfig[@"font"];
73 | if (font) {
74 | }
75 |
76 | id offset = jsConfig[@"offset"];
77 | mdic[kDXTopBarOffset] = @([offset floatValue]);
78 |
79 | return mdic;
80 | }
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/ios/Classes/DXFileManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DXFileManager.h
3 | // DXFileManager
4 | //
5 | // Created by xiekw on 15/2/10.
6 | // Copyright (c) 2015年 xiekw. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DXFileManager : NSObject
12 |
13 | /*usual paths*/
14 |
15 | + (NSString *)cachesDirectory;
16 |
17 | + (NSString *)documentsDirectory;
18 |
19 | + (NSString *)libraryDirectory;
20 |
21 | + (NSString *)mainBundleDirectory;
22 |
23 | + (NSString *)temporaryDirectory;
24 |
25 | /*create, delete, move, copy*/
26 |
27 | //create directory
28 | +(BOOL)createDirectoriesForPath:(NSString *)path error:(NSError **)error;
29 | //write file, it will overide the file exist, it the path is the same
30 | +(BOOL)writeFileAtPath:(NSString *)path content:(NSObject *)content;
31 |
32 | //delete directory or file
33 | +(BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;
34 | //delete file
35 | +(BOOL)removeFilesInDirectoryAtPath:(NSString *)path withExtension:(NSString *)extension;
36 | //The predicate is for the array in this path.
37 | +(BOOL)removeFilesInDirectoryAtPath:(NSString *)path withPredicate:(NSPredicate *)predicate;
38 |
39 | //Move, if the Item is a file and Cover == YES, it will remove the origin and use the new one.
40 | //If the item is a directory, cover makes none sense
41 | +(BOOL)moveItemAtPath:(NSString *)path toPath:(NSString *)toPath ifCover:(BOOL)cover error:(NSError **)error;
42 |
43 | //Use the same rule as moveItem API
44 | + (BOOL)renameItemAtPath:(NSString *)path withName:(NSString *)name error:(NSError **)error;
45 |
46 | //copy
47 | +(BOOL)copyItemAtPath:(NSString *)path toPath:(NSString *)toPath ifCover:(BOOL)cover error:(NSError **)error;
48 |
49 | /*calculate size*/
50 |
51 | //the size of item at path, if the path is directory, it will recursively calculate the size
52 | + (uint64_t)totalSizeOfItemAtPath:(NSString *)path recursively:(BOOL)recursive;
53 |
54 | /*listing the complete paths for the path, the preicate items is the whole path not the subpath, so maybe you need to use [path pathExtension] to get last extension*/
55 | + (NSArray *)listingPathsAtPath:(NSString *)path recursively:(BOOL)recursive withPredicate:(NSPredicate *)predicate;
56 |
57 | + (void)getDiskUsage:(void (^)(uint64_t freeSpace, uint64_t totalSpace, uint64_t myAppUsed))block;
58 |
59 | + (dispatch_source_t)monitorFileChangesInDirectory:(NSString *)dirPath changeHandler:(dispatch_block_t)handler;
60 |
61 | + (NSString *)mimeTypeForFileExtension:(NSString *)ext;
62 |
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/js/actions/notifications.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Platform = require('Platform')
4 | const VibrationIOS = require('VibrationIOS')
5 | const { updateInstallation } = require('./installation')
6 | const PushNotification = require('react-native-push-notification');
7 |
8 | function normalizeData(s) {
9 | if (s && typeof s === 'object') {
10 | return s
11 | }
12 | try {
13 | return JSON.parse(s)
14 | } catch (e) {
15 | return {}
16 | }
17 | }
18 |
19 | async function storeDeviceToken(deviceToken) {
20 | const pushType = Platform.OS === 'android' ? 'gcm' : undefined
21 | await updateInstallation({
22 | pushType,
23 | deviceToken,
24 | deviceTokenLastModified: Date.now(),
25 | })
26 | return {
27 | type: 'REGISTERED_PUSH_NOTIFICATIONS',
28 | }
29 | }
30 |
31 | function checkPushNotifications() {
32 | return dispatch => {
33 | PushNotification.checkPermissions(permission => {
34 | const disabled = permission.alert < 1
35 | dispatch({
36 | type: 'CHECK_PUSH_NOTIFICATIONS',
37 | disabled
38 | })
39 | })
40 | }
41 | }
42 |
43 | function turnOnPushNotifications() {
44 | return {
45 | type: 'TURNED_ON_PUSH_NOTIFICATIONS',
46 | }
47 | }
48 |
49 | function skipPushNotifications() {
50 | return {
51 | type: 'SKIPPED_PUSH_NOTIFICATIONS',
52 | }
53 | }
54 |
55 | function receivePushNotification(notification) {
56 | return (dispatch) => {
57 | let { foreground, message } = notification
58 | const data = normalizeData(notification.data)
59 |
60 | message = message.split(',')[0]
61 |
62 | if (!foreground) {
63 | dispatch(showNotificationView(message))
64 | }
65 |
66 | if (foreground) {
67 | dispatch(showNotificationView(message))
68 |
69 |
70 | if (Platform.OS === 'ios') {
71 | VibrationIOS.vibrate()
72 | }
73 | }
74 |
75 | if (data.e /* ephemeral */) {
76 | return
77 | }
78 |
79 | const timestamp = new Date().getTime()
80 | dispatch({
81 | type: 'RECEIVED_PUSH_NOTIFICATION',
82 | notification: {
83 | text: message,
84 | url: data.url,
85 | time: timestamp,
86 | },
87 | })
88 | }
89 | }
90 |
91 | function markAllNotificationsAsSeen() {
92 | return {
93 | type: 'SEEN_ALL_NOTIFICATIONS',
94 | }
95 | }
96 |
97 | module.exports = {
98 | turnOnPushNotifications,
99 | storeDeviceToken,
100 | skipPushNotifications,
101 | receivePushNotification,
102 | markAllNotificationsAsSeen,
103 | checkPushNotifications
104 | }
105 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Disabling obfuscation is useful if you collect stack traces from production crashes
20 | # (unless you are using a system that supports de-obfuscate the stack traces).
21 | -dontobfuscate
22 |
23 | # React Native
24 |
25 | # Keep our interfaces so they can be used by other ProGuard rules.
26 | # See http://sourceforge.net/p/proguard/bugs/466/
27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
30 |
31 | # Do not strip any method/class that is annotated with @DoNotStrip
32 | -keep @com.facebook.proguard.annotations.DoNotStrip class *
33 | -keep @com.facebook.common.internal.DoNotStrip class *
34 | -keepclassmembers class * {
35 | @com.facebook.proguard.annotations.DoNotStrip *;
36 | @com.facebook.common.internal.DoNotStrip *;
37 | }
38 |
39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
40 | void set*(***);
41 | *** get*();
42 | }
43 |
44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; }
46 | -keepclassmembers,includedescriptorclasses class * { native ; }
47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
50 |
51 | -dontwarn com.facebook.react.**
52 |
53 | # okhttp
54 |
55 | -keepattributes Signature
56 | -keepattributes *Annotation*
57 | -keep class okhttp3.** { *; }
58 | -keep interface okhttp3.** { *; }
59 | -dontwarn okhttp3.**
60 |
61 | # okio
62 |
63 | -keep class sun.misc.Unsafe { *; }
64 | -dontwarn java.nio.file.*
65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
66 | -dontwarn okio.**
67 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/js/pages/SearchHistory.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | View,
8 | Text,
9 | TouchableOpacity,
10 | } from 'react-native'
11 | import Color from '../constants/Colors'
12 | import { connect } from 'react-redux'
13 | import {
14 | searchSearchHistory,
15 | removeSearchHistory,
16 | startSearch
17 | } from '../actions/search'
18 | import IconCell from '../common/IconCell'
19 |
20 | class SearchHistory extends Component {
21 | componentDidMount() {
22 | this.props.getSearchHistory()
23 | }
24 |
25 | render() {
26 | let { history, onRemoveSearchHistory, startSearch } = this.props
27 |
28 | let clearAll
29 | if (history && history.length) {
30 | history = history.map((his, i) => {
31 | return (
32 | startSearch(his)}
38 | />
39 | )
40 | })
41 |
42 | clearAll = (
43 |
46 | clear all history
47 |
48 | )
49 | }
50 |
51 | return (
52 |
53 | {history}
54 | {clearAll}
55 |
56 | )
57 | }
58 | }
59 |
60 | SearchHistory.propTypes = {
61 | history: PropTypes.array,
62 | onRemoveSearchHistory: PropTypes.func,
63 | getSearchHistory: PropTypes.func,
64 | startSearch: PropTypes.func
65 | }
66 | SearchHistory.defaultProps = {
67 | history: ['金馆长', '教皇', '丘比龙']
68 | }
69 |
70 | const mapStateToProps = state => {
71 | return {
72 | history: state.search.history
73 | }
74 | }
75 |
76 | const mapDispatchToProps = dispatch => {
77 | return {
78 | onRemoveSearchHistory: his => dispatch(removeSearchHistory(his)),
79 | getSearchHistory: _ => dispatch(searchSearchHistory()),
80 | startSearch: text => dispatch(startSearch(text, 'HISTORY'))
81 | }
82 | }
83 |
84 | export default connect(mapStateToProps, mapDispatchToProps)(SearchHistory)
85 | module.exports.__cards__ = (define) => {
86 | define('normal', _ => )
87 | }
88 |
89 | const styles = StyleSheet.create({
90 | container: {
91 | flexDirection: 'column',
92 | },
93 | removeAllHis: {
94 | flexDirection: 'row',
95 | justifyContent: 'center',
96 | alignItems: 'center',
97 | padding: 10,
98 | },
99 | clearAll: {
100 | color: Color.green
101 | }
102 | })
--------------------------------------------------------------------------------
/js/services/Storage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by xiekaiwei on 16/8/24.
3 | */
4 |
5 | import { AsyncStorage } from 'react-native'
6 | import Parse from 'parse/react-native'
7 |
8 | const CURRENT_USER_KEY = 'CURRENT_USER_KEY_GITFEED'
9 | const SEARCH_HISTORY_KEY = 'SEARCH_HISTORY_KEY'
10 |
11 | export const saveUser = async user => {
12 | await AsyncStorage.setItem(CURRENT_USER_KEY, JSON.stringify(user))
13 | return user
14 | }
15 |
16 | export const getCurrentUser = async() => {
17 | const userString = await AsyncStorage.getItem(CURRENT_USER_KEY)
18 | return JSON.parse(userString)
19 | }
20 |
21 | export const getSearchHistory = async() => {
22 | return await _getAllObjects(SEARCH_HISTORY_KEY, obj => !!obj && obj.length > 0, 4)
23 | }
24 |
25 | export const insertSearchRecord = async(item) => {
26 | if (!item || !item.length) return
27 | return await _insertObject(SEARCH_HISTORY_KEY, item, item => item, true)
28 | }
29 |
30 | export const removeSearchRecord = async(item) => {
31 | if (item) {
32 | return await _removeObject(SEARCH_HISTORY_KEY, item, item => item)
33 | } else {
34 | return await AsyncStorage.multiRemove([SEARCH_HISTORY_KEY])
35 | }
36 | }
37 |
38 | const _getAllKeys = async(key) => {
39 | const allKeys = await AsyncStorage.getItem(key)
40 | return JSON.parse(allKeys) || []
41 | }
42 |
43 | const _getAllObjects = async(key, prd = obj => !!obj, maxCount = 1000) => {
44 | const allKeys = await _getAllKeys(key)
45 | if (!allKeys) return null
46 |
47 | let allObjs = await AsyncStorage.multiGet(allKeys)
48 | if (!allObjs) return null
49 |
50 |
51 | allObjs = allObjs
52 | .map(kv => JSON.parse(kv[1]))
53 | .reduce((a, b) => a.concat(b), [])
54 | .filter(prd)
55 | if (allKeys.length > maxCount) {
56 | const nextAllKeys = allKeys.slice(0, maxCount)
57 | await AsyncStorage.setItem(key, JSON.stringify(nextAllKeys))
58 | }
59 |
60 | return allObjs
61 | }
62 |
63 | const _insertObject = async(key, obj, objAsKey = obj => obj.title, distinct = false) => {
64 | const objKey = objAsKey(obj)
65 | if (!objKey) throw new Error('Storage insert this obj does not have key!')
66 |
67 | let allKeys = await _getAllKeys(key)
68 | if (distinct) allKeys = allKeys.filter(k => objKey != k)
69 |
70 | allKeys.unshift(objKey)
71 |
72 | await AsyncStorage.setItem(key, JSON.stringify(allKeys))
73 | await AsyncStorage.setItem(objKey, JSON.stringify(obj))
74 |
75 | return obj
76 | }
77 |
78 | const _removeObject = async(key, obj, objAsKey = obj => obj.title) => {
79 | const objKey = objAsKey(obj)
80 | if (!objKey) throw new Error('Storage remove this obj does not have key!')
81 |
82 | let allKeys = await _getAllKeys(key)
83 | allKeys = allKeys.filter(k => k != objKey)
84 |
85 | await AsyncStorage.setItem(key, JSON.stringify(allKeys))
86 | await AsyncStorage.removeItem(objKey)
87 |
88 | return obj
89 | }
--------------------------------------------------------------------------------
/js/common/TabBarAndroid.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import Icon from 'react-native-vector-icons/Ionicons'
6 | import {
7 | StyleSheet,
8 | Text,
9 | View,
10 | TouchableOpacity,
11 | } from 'react-native'
12 | import Colors from './Colors'
13 |
14 | var FacebookTabBar = React.createClass({
15 | selectedTabIcons: [],
16 | unselectedTabIcons: [],
17 |
18 | propTypes: {
19 | goToPage: React.PropTypes.func,
20 | activeTab: React.PropTypes.number,
21 | tabs: React.PropTypes.array
22 | },
23 |
24 | /*
25 | {
26 | pageId: 'HomePage',
27 | name: '流行',
28 | iconSelected: 'ios-flame',
29 | iconUnSelected: 'ios-flame-outline',
30 | },
31 | */
32 | renderTabOption(tabItem, page) {
33 | const isTabActive = this.props.activeTab === page
34 | const color = isTabActive ? Colors.green : Colors.black
35 | const { name, iconSelected, iconUnSelected } = tabItem
36 | const icon = isTabActive ? iconSelected : iconUnSelected
37 |
38 | return (
39 | this.props.goToPage(page)}
42 | style={styles.tab}>
43 | { this.selectedTabIcons[page] = icon }}
45 | style={styles.tabItem}
46 | >
47 |
48 |
49 | {name}
50 |
51 |
52 |
53 | )
54 | },
55 |
56 | setAnimationValue({ value }) {
57 | var currentPage = this.props.activeTab
58 |
59 | this.unselectedTabIcons.forEach((icon, i) => {
60 | var iconRef = icon
61 |
62 | if (!icon.setNativeProps && icon !== null) {
63 | iconRef = icon.refs.icon_image
64 | }
65 |
66 | if (value - i >= 0 && value - i <= 1) {
67 | iconRef.setNativeProps({ opacity: value - i })
68 | }
69 | if (i - value >= 0 && i - value <= 1) {
70 | iconRef.setNativeProps({ opacity: i - value })
71 | }
72 | })
73 | },
74 |
75 | render() {
76 | return (
77 |
78 |
79 | {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))}
80 |
81 |
82 | )
83 | },
84 | })
85 |
86 | const styles = StyleSheet.create({
87 | tab: {
88 | flex: 1,
89 | alignItems: 'center',
90 | justifyContent: 'center',
91 | },
92 | tabItem: {
93 | flexDirection: 'column',
94 | alignItems: 'center',
95 | },
96 | tabs: {
97 | height: 49,
98 | flexDirection: 'row',
99 | paddingTop: 5,
100 | borderTopWidth: 0.5,
101 | borderTopColor: Colors.gray,
102 | },
103 | })
104 |
105 | export default FacebookTabBar
--------------------------------------------------------------------------------
/js/common/SettingsCell.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | View,
7 | Text,
8 | StyleSheet,
9 | TouchableHighlight
10 | } from 'react-native'
11 | import Icon from 'react-native-vector-icons/Ionicons'
12 | import Touchable from '../common/F8Touchable'
13 |
14 | const SettingsCell = props => {
15 | let iconComponent
16 | if (props.iconName) {
17 | iconComponent =
22 | }
23 |
24 | let arrow
25 | if (props.hasArrow) {
26 | arrow =
31 | }
32 |
33 | let rightText
34 | if (props.rightText) {
35 | rightText =
41 | {props.rightText}
42 |
43 | }
44 |
45 | return (
46 |
49 |
50 | {iconComponent}
51 |
52 |
53 | {props.settingName}
54 |
55 |
56 |
57 | {rightText}
58 | {arrow}
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | SettingsCell.propTypes = {
66 | onPress: PropTypes.func,
67 | iconName: PropTypes.string,
68 | iconColor: PropTypes.string,
69 | settingName: PropTypes.string,
70 | hasArrow: PropTypes.bool,
71 | rightText: PropTypes.string,
72 | }
73 | SettingsCell.defaultProps = {
74 | iconColor: '#00bfff',
75 | settingName: 'Settings',
76 | onPress: _ => {},
77 | hasArrow: true,
78 | rightText: ''
79 | }
80 |
81 | export default SettingsCell
82 |
83 | const ICON_SIZE = 30
84 | const styles = StyleSheet.create({
85 | userTouch: {
86 | marginTop: 20
87 | },
88 | user: {
89 | padding: 8,
90 | paddingLeft: 10,
91 | paddingRight: 10,
92 | backgroundColor: 'white',
93 | flexDirection: 'row',
94 | alignItems: 'center',
95 | borderTopWidth: 1,
96 | borderBottomWidth: 1,
97 | borderColor: '#EDECF1'
98 | },
99 | nameInfo: {
100 | flexDirection: 'column',
101 | marginLeft: 0,
102 | justifyContent: 'center',
103 | flex: 1
104 | },
105 | name: {
106 | color: 'black',
107 | fontSize: 17
108 | },
109 | right: {
110 | flexDirection: 'row',
111 | marginRight: 10
112 | },
113 | arrow: {
114 | width: ICON_SIZE,
115 | height: ICON_SIZE,
116 | },
117 | settings: {
118 | height: 44
119 | }
120 | })
--------------------------------------------------------------------------------
/js/pages/IssueDetailPage.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | PropTypes,
4 | } from 'react'
5 | import {
6 | StyleSheet,
7 | View,
8 | TouchableOpacity,
9 | Text,
10 | Image,
11 | ActionSheetIOS,
12 | } from 'react-native'
13 | import defaultHtml from '../common/markdown/issue-html'
14 | import WebView from '../common/WebView'
15 | import Layout from '../constants/Layout'
16 | import { LoadingPlaceholder, ErrorPlaceholder } from '../common/Placeholder'
17 |
18 | class IssueDetailPage extends Component {
19 | static route = {
20 | navigationBar: {
21 | title(params) {
22 | if (params.repo) return `${params.repo.title}`
23 | },
24 | },
25 | }
26 |
27 | state = {
28 | loading: true,
29 | error: null,
30 | source: { html: '', baseUrl: 'about:blank', }
31 | }
32 |
33 | _issueHtml(originHtml) {
34 | const start = '