From 6c0d5cbe4fb70a83b63a659bf02393ef8b7cc621 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Wed, 27 Mar 2019 16:21:23 +0800 Subject: [PATCH 01/20] usercenter --- js/common/MORE_MENU.js | 23 ++++++ js/navigator/DynamicTabNavigator.js | 11 ++- js/page/FavoritePage.js | 25 +++++- js/page/Mypage.js | 121 ++++++++++++++++++++-------- js/page/PopularPage.js | 31 ++++++- js/page/TrendingPage.js | 29 ++++++- js/redux/action/action_types.js | 6 +- js/redux/action/favorite/index.js | 6 +- js/redux/action/index.js | 8 +- js/redux/action/popular/index.js | 24 ++++++ js/redux/action/trending/index.js | 24 ++++++ js/redux/reducer/favorite/index.js | 1 - js/redux/reducer/pouplar/index.js | 8 ++ js/redux/reducer/trending/index.js | 9 +++ js/res/GobalStyles.js | 18 +++++ js/util/EventTypes.js | 5 ++ js/util/ViewUtil.js | 59 +++++++++++++- package.json | 1 + yarn.lock | 9 ++- 19 files changed, 359 insertions(+), 59 deletions(-) create mode 100644 js/common/MORE_MENU.js create mode 100644 js/res/GobalStyles.js create mode 100644 js/util/EventTypes.js diff --git a/js/common/MORE_MENU.js b/js/common/MORE_MENU.js new file mode 100644 index 0000000..2870cbf --- /dev/null +++ b/js/common/MORE_MENU.js @@ -0,0 +1,23 @@ +/** +* @name MORE_MENU +* @param {CLIPBOARD} +* @author Ethan +* @date 2019-03-27 13:12:58 +*/ +import Octicons from 'react-native-vector-icons/Octicons'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; +import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; +export const MORE_MENU = { + Custom_Language: {name:'自定义语言', Icons:Ionicons, icon: 'md-checkbox-outline'}, + Sort_Language: {name:'语言排序', Icons:MaterialCommunityIcons, icon: 'sort'}, + Custom_Theme: {name:'自定义主题', Icons:Ionicons, icon: 'ios-color-palette'}, + Custom_key: {name:'自定义标签', Icons:Ionicons, icon: 'md-checkbox-outline'}, + Sort_key: {name:'标签排序', Icons:MaterialCommunityIcons, icon: 'sort'}, + Remove_key: {name:'标签移除', Icons:Ionicons, icon: 'md-remove'}, + About_Author: {name:'关于作者', Icons:Octicons, icon: 'smiley'}, + About: {name:'关于', Icons:Ionicons, icon: 'logo-github'}, + Tutorial: {name:'教程', Icons:Ionicons, icon: 'ios-bookmarks'}, + Feedback: {name:'反馈', Icons:MaterialIcons, icon: 'feedback'}, + Share: {name:'分享', Icons:Ionicons, icon: 'md-share'}, +}; diff --git a/js/navigator/DynamicTabNavigator.js b/js/navigator/DynamicTabNavigator.js index 00eb492..9529800 100644 --- a/js/navigator/DynamicTabNavigator.js +++ b/js/navigator/DynamicTabNavigator.js @@ -23,6 +23,8 @@ import Mypage from '../page/Mypage'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import Ionicons from 'react-native-vector-icons/Ionicons'; import Entypo from 'react-native-vector-icons/Entypo'; +import EventBus from 'react-native-event-bus'; +import EventTypes from '../util/EventTypes'; const TABS = { PopularPage:{ @@ -102,7 +104,14 @@ export default class DynamicTabNavigator extends Component { } render() { const Tab = this._tabNavigator(); - return + return { + EventBus.getInstance().fireEvent(EventTypes.bottom_tab_select, { // 发送底部tab切换事件 + from: prevState.index, + to: newState.index + }) + }} + /> } } diff --git a/js/page/FavoritePage.js b/js/page/FavoritePage.js index 3dedba4..a9c390f 100644 --- a/js/page/FavoritePage.js +++ b/js/page/FavoritePage.js @@ -24,6 +24,8 @@ import PopularItem from '../common/PopularItem'; import TrendingItem from '../common/TrendingItem'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; +import EventTypes from '../util/EventTypes'; +import EventBus from 'react-native-event-bus'; // 顶部导航tab标签配置 @@ -102,12 +104,20 @@ class FavoriteTab extends Component { this.favoriteDao = new FavoriteDao(flag); } componentDidMount() { - this.loadData(); + this.loadData(true); + EventBus.getInstance().addListener(EventTypes.bottom_tab_select, this.listener = data => { + if (data.to === 2) { + this.loadData(false); + } + }); + } + componentWillMount() { + // 移除监听器 + EventBus.getInstance().removeListener(this.listener); } loadData (isShowLoading) { // 加载数据 const {onLoadFavoriteData} = this.props; - console.log(this.storeName) onLoadFavoriteData(this.storeName, isShowLoading); } _store () { @@ -123,6 +133,14 @@ class FavoriteTab extends Component { } return store; } + onFavorite (item, isFavorite) { + FavoriteUtil.onFavorite(this.favoriteDao, item, isFavorite, this.storeName); + if (this.storeName === FLAG_STORAGE.flag_popular) { + EventBus.getInstance().fireEvent(EventTypes.favorite_changed_popular); + } else { + EventBus.getInstance().fireEvent(EventTypes.favorite_changed_trending); + } + } renderItem (data) { const item = data.item; const Item = this.storeName === FLAG_STORAGE.flag_popular ? PopularItem : TrendingItem; @@ -135,7 +153,7 @@ class FavoriteTab extends Component { callback }, 'DetailPage') }} - onFavorite={(item, isFavorite) => FavoriteUtil.onFavorite(this.favoriteDao, item, isFavorite, this.storeName)} + onFavorite={(item, isFavorite) => this.onFavorite(item, isFavorite)} /> } genIndicator () { @@ -190,7 +208,6 @@ const styles = StyleSheet.create({ }, labelStyle: { fontSize: 13, - // margin: 0, }, dataText: { flex: 1 diff --git a/js/page/Mypage.js b/js/page/Mypage.js index 55822ec..4256212 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -1,11 +1,14 @@ import React, {Component} from 'react'; -import {StyleSheet, View, Button, Text, TouchableOpacity} from 'react-native'; +import {StyleSheet, ScrollView, View, Button, Text, TouchableOpacity} from 'react-native'; import {connect} from 'react-redux'; import actions from '../redux/action'; import Feather from 'react-native-vector-icons/Feather'; import Ionicons from 'react-native-vector-icons/Ionicons'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; +import { MORE_MENU } from '../common/MORE_MENU'; +import GobalStyles from '../res/GobalStyles'; +import ViewUtil from '../util/ViewUtil'; const THEME_COLOR = '#f33' @@ -15,34 +18,11 @@ type Props = {}; {onThemeChange: actions.onThemeChange} ) export default class My extends Component { - getRightButton () { - return ( - - {}} - > - - - - - - ) + onClick (menu) { + } - getLeftButton (callback) { - return ( - - - - ) + getItem (menu) { + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); } render() { let statusBar = { @@ -53,20 +33,93 @@ export default class My extends Component { title={'我的'} statusBar={statusBar} style={{backgroundColor:THEME_COLOR}} - rightButton={this.getRightButton()} - leftButton={this.getLeftButton()} /> return ( - + {navigationBar} - MyPage + + this.onClick(MORE_MENU.About)} + > + + + GitHub Popular + + + + + {/* 教程 */} + {this.getItem(MORE_MENU.Tutorial)} + 趋势管理 + {/* 自定义语言 */} + {this.getItem(MORE_MENU.Custom_Language)} + + {/* 语言排序 */} + {this.getItem(MORE_MENU.Sort_Language)} + + + {/* 最热管理 */} + 最热管理 + {/* 自定义标签 */} + {this.getItem(MORE_MENU.Custom_key)} + + {/* 标签排序 */} + {this.getItem(MORE_MENU.Sort_key)} + + {/* 标签移除 */} + {this.getItem(MORE_MENU.Remove_key)} + + + {/* 设置 */} + 设置 + {/* 自定义主题 */} + {this.getItem(MORE_MENU.Custom_Theme)} + + {/* 关于作者 */} + {this.getItem(MORE_MENU.About_Author)} + + {/* 反馈 */} + {this.getItem(MORE_MENU.Feedback)} + ); } } const styles = StyleSheet.create({ - container: { - flex: 1, + about_left: { + flexDirection: 'row', + alignItems: 'center', + }, + item:{ + backgroundColor: 'white', + padding: 10, + height: 90, + alignItems: 'center', + justifyContent: 'space-between', + flexDirection: 'row', + }, + groupTitle: { + marginLeft: 10, + marginTop: 5, + marginBottom: 5, + fontSize: 12, + color: 'gray' } }); diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index 34b2b01..4fbf7c6 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -24,7 +24,9 @@ import PopularItem from '../common/PopularItem'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; -import { getAccountList } from '../axios/api/account'; +//用于页面之间通讯 +import EventTypes from '../util/EventTypes'; +import EventBus from 'react-native-event-bus'; // 顶部导航tab标签配置 const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; @@ -100,7 +102,8 @@ export default class Popuilar extends Component { state=>state, { onLoadPopularData: actions.onLoadPopularData, - onLoadMorePopular: actions.onLoadMorePopular + onLoadMorePopular: actions.onLoadMorePopular, + onFlushPopularFavorite: actions.onFlushPopularFavorite } ) class PopuilarTab extends Component { @@ -108,19 +111,39 @@ class PopuilarTab extends Component { super(props); const {tabLabel} = this.props; this.storeName = tabLabel; + this.isFavoriteChanged = false; } componentDidMount() { this.loadData(); + EventBus.getInstance().addListener(EventTypes.favorite_changed_popular, this.favoriteChangeListener = () => { + // 收藏页面状态变化的通知 + this.isFavoriteChanged = true; + }); + EventBus.getInstance().addListener(EventTypes.bottom_tab_select, this.botomTabSelectListener = (data) => { + // 底部tab切换的通知 + if (data.to === 0 && this.isFavoriteChanged) { + this.loadData(null, true); + } + }); + } + componentWillMount() { + // 移除监听器 + EventBus.getInstance().removeListener(this.favoriteChangeListener); + EventBus.getInstance().removeListener(this.botomTabSelectListener); } - loadData (loadMore) { + loadData (loadMore, refreshFavorite) { + // refreshFavorite 是否刷新收藏状态 // 加载数据 - const {onLoadPopularData, onLoadMorePopular} = this.props; + const {onLoadPopularData, onLoadMorePopular, onFlushPopularFavorite} = this.props; const store = this._store(); const url = this.genFetchUrl(this.storeName); if (loadMore) { onLoadMorePopular(this.storeName,++store.pageIndex, PAEG_SIZE, store.items, favoriteDao, callback => { this.refs.toast.show('没有更多了'); }) + } else if (refreshFavorite) { + // 从favorite页面回跳至popular页面时,刷新popular页面的收藏状态 + onFlushPopularFavorite(this.storeName,store.pageIndex, PAEG_SIZE, store.items, favoriteDao); } else { onLoadPopularData(this.storeName, url, PAEG_SIZE, favoriteDao); } diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index 98b76ea..d23fa85 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -37,7 +37,9 @@ import NavigationBar from '../common/NavigationBar'; // 自定义弹窗 import TrendingDialog, {TimeSpans} from '../common/TrendingDialog'; -import { getAccountList } from '../axios/api/account'; +//用于页面之间通讯 +import EventTypes from '../util/EventTypes'; +import EventBus from 'react-native-event-bus'; // 顶部导航tab标签配置 const TAB_NAMES = ['All', 'C', 'C#', 'PHP', 'Javascript']; @@ -166,7 +168,8 @@ export default class Trending extends Component { state=>state, { onLoadTrendingData: actions.onLoadTrendingData, - onLoadMoreTrending: actions.onLoadMoreTrending + onLoadMoreTrending: actions.onLoadMoreTrending, + onFlushTrendingFavorite: actions.onFlushTrendingFavorite } ) class TrendingTab extends Component { @@ -175,6 +178,8 @@ class TrendingTab extends Component { const {tabLabel, timeSpan} = this.props; this.storeName = tabLabel; this.timeSpan = timeSpan; + // 是否刷新页面的收藏状态 + this.isFavoriteChanged = false; } componentDidMount() { this.loadData(); @@ -182,21 +187,37 @@ class TrendingTab extends Component { this.timeSpan = timeSpan; this.loadData(); }); + + EventBus.getInstance().addListener(EventTypes.favorite_changed_trending, this.favoriteChangeListener = () => { + // 收藏页面状态变化的通知 + this.isFavoriteChanged = true; + }); + EventBus.getInstance().addListener(EventTypes.bottom_tab_select, this.botomTabSelectListener = (data) => { + // 底部tab切换的通知 + if (data.to === 1 && this.isFavoriteChanged) { + this.loadData(null, true); + } + }); } componentWillUnmount () { if (this.timeSpanChangeListener) { this.timeSpanChangeListener.remove(); } + // 移除监听器 + EventBus.getInstance().removeListener(this.favoriteChangeListener); + EventBus.getInstance().removeListener(this.botomTabSelectListener); } - loadData (loadMore) { + loadData (loadMore, refreshFavorite) { // 加载数据 - const {onLoadTrendingData, onLoadMoreTrending} = this.props; + const {onLoadTrendingData, onLoadMoreTrending, onFlushTrendingFavorite} = this.props; const store = this._store(); const url = this.genFetchUrl(this.storeName); if (loadMore) { onLoadMoreTrending(this.storeName,++store.pageIndex, PAEG_SIZE, store.items, favoriteDao, callback => { this.refs.toast.show('没有更多了'); }) + } else if (refreshFavorite) { + onFlushTrendingFavorite(this.storeName, store.pageIndex, PAEG_SIZE, store.items, favoriteDao) } else { onLoadTrendingData(this.storeName, url, PAEG_SIZE, favoriteDao); } diff --git a/js/redux/action/action_types.js b/js/redux/action/action_types.js index 43b0b52..380979c 100644 --- a/js/redux/action/action_types.js +++ b/js/redux/action/action_types.js @@ -6,12 +6,14 @@ export default { POPULAR_REFRESH_SUCCESS: 'POPULAR_REFRESH_SUCCESS', //popular数据加载成功 POPULAR_LOAD_MORE_SUCCESS: 'POPULAR_LOAD_MORE_SUCCESS', //加载更多 POPULAR_LOAD_MORE_FAIL: 'POPULAR_LOAD_MORE_FAIL', // + FLUSH_POPULAR_FAVORITE: 'FLUSH_POPULAR_FAVORITE', // 刷新popular页面的收藏状态 TRENDING_REFRESH: 'TRENDING_REFRESH', //TRENDING数据刷新 TRENDING_REFRESH_FAIL: 'TRENDING_REFRESH_FAIL', //TRENDING数据加载失败 TRENDING_REFRESH_SUCCESS: 'TRENDING_REFRESH_SUCCESS', //TRENDING数据加载成功 TRENDING_LOAD_MORE_SUCCESS: 'TRENDING_LOAD_MORE_SUCCESS', //TRENDING加载更多 TRENDING_LOAD_MORE_FAIL: 'TRENDING_LOAD_MORE_FAIL', //TRENDING加载更多失败 - FAVORITE_LOAD_DATA: 'FAVORITE_LOAD_DATA', - FAVORITE_LOAD_SUCCESS: 'FAVORITE_LOAD_SUCCESS', + FLUSH_TRENDING_FAVORITE: 'FLUSH_TRENDING_FAVORITE', // 刷新trending页面的收藏状态 + FAVORITE_LOAD_DATA: 'FAVORITE_LOAD_DATA', // 加载favorite页面数据 + FAVORITE_LOAD_SUCCESS: 'FAVORITE_LOAD_SUCCESS', // favorite数据加载成功 FAVORITE_LOAD_FAIL: 'FAVORITE_LOAD_FAIL', } \ No newline at end of file diff --git a/js/redux/action/favorite/index.js b/js/redux/action/favorite/index.js index 4b85e91..868df66 100644 --- a/js/redux/action/favorite/index.js +++ b/js/redux/action/favorite/index.js @@ -11,13 +11,15 @@ import ProjectModel from '../../../model/ProjectModel'; export function onLoadFavoriteData(flag, isShowLoading) { return dispatch => { // 派发刷新状态 - dispatch({type:Types.FAVORITE_LOAD_DATA, storeName: flag}); + if (isShowLoading) { + // 根据isShowLoading决定是否显示loading动画 + dispatch({type:Types.FAVORITE_LOAD_DATA, storeName: flag}); + } new FavoriteDao(flag).getAllItems().then(items => { let resultData = []; for (let i = 0, len = items.length; i < len; i++) { resultData.push(new ProjectModel(items[i], true)); } - console.log(resultData) dispatch({ type: Types.FAVORITE_LOAD_SUCCESS, projectModels: resultData, diff --git a/js/redux/action/index.js b/js/redux/action/index.js index 7981ef3..69388c3 100644 --- a/js/redux/action/index.js +++ b/js/redux/action/index.js @@ -1,6 +1,6 @@ import {onThemeChange} from './theme'; -import {onLoadPopularData, onLoadMorePopular} from './popular'; -import {onLoadTrendingData, onLoadMoreTrending} from './trending'; +import {onLoadPopularData, onLoadMorePopular, onFlushPopularFavorite} from './popular'; +import {onLoadTrendingData, onLoadMoreTrending, onFlushTrendingFavorite} from './trending'; import {onLoadFavoriteData} from './favorite'; export default { @@ -9,5 +9,7 @@ export default { onLoadMorePopular, onLoadTrendingData, onLoadMoreTrending, - onLoadFavoriteData + onLoadFavoriteData, + onFlushPopularFavorite, + onFlushTrendingFavorite } \ No newline at end of file diff --git a/js/redux/action/popular/index.js b/js/redux/action/popular/index.js index 90b634c..0af6be4 100644 --- a/js/redux/action/popular/index.js +++ b/js/redux/action/popular/index.js @@ -64,4 +64,28 @@ export function onLoadMorePopular (storeName, pageIndex, pageSize, dataArray=[], } }, 500); } +} + +/** + * 刷新页面收藏状态 + * @param {*} storeName + * @param {*} pageIndex + * @param {*} pageSize + * @param {*} [dataArray=[]] + * @param {*} favoriteDao + * @returns + */ +export function onFlushPopularFavorite (storeName, pageIndex, pageSize, dataArray = [], favoriteDao) { + return dispatch => { + // 本次和载入的最大数量 + let max = pageSize * pageIndex > dataArray.length ? dataArray.length : pageIndex * pageSize; + _projectModels(dataArray.slice(0, max), favoriteDao, data => { + dispatch({ + type: Types.FLUSH_POPULAR_FAVORITE, + storeName: storeName, + pageIndex: pageIndex, + projectModels: data + }) + }) + } } \ No newline at end of file diff --git a/js/redux/action/trending/index.js b/js/redux/action/trending/index.js index 216c4eb..97069bf 100644 --- a/js/redux/action/trending/index.js +++ b/js/redux/action/trending/index.js @@ -67,3 +67,27 @@ export function onLoadMoreTrending (storeName, pageIndex, pageSize, dataArray=[] }, 500); } } + +/** + * 刷新页面收藏状态 + * @param {*} storeName + * @param {*} pageIndex + * @param {*} pageSize + * @param {*} [dataArray=[]] + * @param {*} favoriteDao + * @returns + */ +export function onFlushTrendingFavorite (storeName, pageIndex, pageSize, dataArray = [], favoriteDao) { + return dispatch => { + // 本次和载入的最大数量 + let max = pageSize * pageIndex > dataArray.length ? dataArray.length : pageIndex * pageSize; + _projectModels(dataArray.slice(0, max), favoriteDao, data => { + dispatch({ + type: Types.FLUSH_TRENDING_FAVORITE, + storeName: storeName, + pageIndex: pageIndex, + projectModels: data + }) + }) + } +} \ No newline at end of file diff --git a/js/redux/reducer/favorite/index.js b/js/redux/reducer/favorite/index.js index 7573311..fd3898a 100644 --- a/js/redux/reducer/favorite/index.js +++ b/js/redux/reducer/favorite/index.js @@ -21,7 +21,6 @@ const defaultState = {} * @returns */ export default function onAction (state=defaultState, action) { - console.log(action) switch (action.type) { // 获取数据 case Types.FAVORITE_LOAD_DATA: diff --git a/js/redux/reducer/pouplar/index.js b/js/redux/reducer/pouplar/index.js index 8bf3331..95a2590 100644 --- a/js/redux/reducer/pouplar/index.js +++ b/js/redux/reducer/pouplar/index.js @@ -60,6 +60,14 @@ export default function onAction (state=defaultState, action) { hideLoadingMore: true, pageIndex: action.pageIndex }}; + case Types.FLUSH_POPULAR_FAVORITE: + return { + ...state, + [action.storeName]: { + ...state[action.storeName], + projectModels: action.projectModels + } + }; default: return state; } diff --git a/js/redux/reducer/trending/index.js b/js/redux/reducer/trending/index.js index 5c9b761..6c2cc0d 100644 --- a/js/redux/reducer/trending/index.js +++ b/js/redux/reducer/trending/index.js @@ -60,6 +60,15 @@ export default function onAction (state=defaultState, action) { hideLoadingMore: true, pageIndex: action.pageIndex }}; + // 刷新趋势页面的收藏状态 + case Types.FLUSH_TRENDING_FAVORITE: + return { + ...state, + [action.storeName]: { + ...state[action.storeName], + projectModels: action.projectModels + } + }; default: return state; } diff --git a/js/res/GobalStyles.js b/js/res/GobalStyles.js new file mode 100644 index 0000000..332df9e --- /dev/null +++ b/js/res/GobalStyles.js @@ -0,0 +1,18 @@ +/** +* @name GobalStyles +* @param {CLIPBOARD} +* @author Ethan +* @date 2019-03-27 14:15:32 +*/ +const BACKGROUND_COLOR = '#F3F3F4'; +export default { + line: { + height: 0.5, + opacity: 0.5, + backgroundColor: 'darkgray' + }, + root_container: { + flex: 1, + backgroundColor: BACKGROUND_COLOR + } +}; \ No newline at end of file diff --git a/js/util/EventTypes.js b/js/util/EventTypes.js new file mode 100644 index 0000000..e75fdf7 --- /dev/null +++ b/js/util/EventTypes.js @@ -0,0 +1,5 @@ +export default { + bottom_tab_select: 'bottom_tab_select', + favorite_changed_popular: 'favorite_changed_popular', + favorite_changed_trending: 'favorite_changed_trending' +} \ No newline at end of file diff --git a/js/util/ViewUtil.js b/js/util/ViewUtil.js index 9c54130..451b145 100644 --- a/js/util/ViewUtil.js +++ b/js/util/ViewUtil.js @@ -1,7 +1,6 @@ import React from 'react'; -import {TouchableOpacity} from 'react-native'; +import {TouchableOpacity, StyleSheet, View, Text} from 'react-native'; import Ionicons from 'react-native-vector-icons/Ionicons'; - export default class ViewUtil { /** * @@ -42,5 +41,59 @@ export default class ViewUtil { /> } - + /** + * + * + * @static + * @param {*} callback 点击item回调函数 + * @param {*} text 显示文办 + * @param {*} color 图标颜色 + * @param {*} Icons react-native-vector-icons组件 + * @param {*} icon 左侧图标 + * @param {*} expandableIcon 右侧图标 + * @memberof ViewUtil + */ + static getSettingItem (callback, text, color, Icons, icon, expandableIcon) { + return ( + + + { + Icons&&icon? + : + } + {text} + + + + ) + } + static getMenuItem (callback, menu, color, expandableIcon) { + return ViewUtil.getSettingItem(callback, menu.name, color, menu.Icons, menu.icon, expandableIcon) + } } + +const styles = StyleSheet.create({ + setting_item_container: { + backgroundColor: 'white', + padding: 10, + height: 60, + alignItems: 'center', + justifyContent: 'space-between', + flexDirection: 'row', + } +}) \ No newline at end of file diff --git a/package.json b/package.json index b9ee4f5..6d9a867 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react": "16.6.3", "react-native": "0.58.6", "react-native-easy-toast": "^1.2.0", + "react-native-event-bus": "^1.0.0", "react-native-gesture-handler": "^1.1.0", "react-native-htmlview": "^0.13.0", "react-native-vector-icons": "^6.3.0", diff --git a/yarn.lock b/yarn.lock index 141774a..2ac9d7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4819,7 +4819,7 @@ prompts@^2.0.1: kleur "^3.0.2" sisteransi "^1.0.0" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "http://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU= @@ -4928,6 +4928,13 @@ react-native-easy-toast@^1.2.0: dependencies: prop-types "^15.5.10" +react-native-event-bus@^1.0.0: + version "1.0.0" + resolved "http://registry.npm.taobao.org/react-native-event-bus/download/react-native-event-bus-1.0.0.tgz#e193b17bb8639b623f1d72e8596e0cd8635b59d1" + integrity sha1-4ZOxe7hjm2I/HXLoWW4M2GNbWdE= + dependencies: + prop-types "^15.5.7" + react-native-gesture-handler@^1.1.0: version "1.1.0" resolved "http://registry.npm.taobao.org/react-native-gesture-handler/download/react-native-gesture-handler-1.1.0.tgz#2a7d545ad2e0ca23adce22b2af441ad360ecccee" From 41c882e07af2af991ff9ed2e8f108179384c51c3 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Thu, 28 Mar 2019 18:19:31 +0800 Subject: [PATCH 02/20] usercenter --- js/navigator/AppNavigator.js | 14 +++ js/page/Mypage.js | 21 +++- js/page/WebViewPage.js | 93 ++++++++++++++ js/page/about/AboutCommon.js | 188 +++++++++++++++++++++++++++++ js/page/about/AboutPage.js | 69 +++++++++++ js/res/data/github_app_config.json | 90 ++++++++++++++ js/res/{ => styles}/GobalStyles.js | 5 +- package.json | 1 + yarn.lock | 9 +- 9 files changed, 485 insertions(+), 5 deletions(-) create mode 100644 js/page/WebViewPage.js create mode 100644 js/page/about/AboutCommon.js create mode 100644 js/page/about/AboutPage.js create mode 100644 js/res/data/github_app_config.json rename js/res/{ => styles}/GobalStyles.js (75%) diff --git a/js/navigator/AppNavigator.js b/js/navigator/AppNavigator.js index d4cee2c..19c2d7e 100644 --- a/js/navigator/AppNavigator.js +++ b/js/navigator/AppNavigator.js @@ -18,6 +18,8 @@ import DetailPage from '../page/DetailPage'; import AxiosDemoPage from '../page/AxiosDemo'; // 网络请求测试页面 import AsyncStorageDemo from '../page/AsyncStorageDemo'; //AsyncStorage测试页面 import DataStorePage from '../page/DataStorePage'; +import WebViewPage from '../page/WebViewPage'; //浏览器页面 +import AboutPage from '../page/about/AboutPage'; export const rootCom = 'Init'; //设置跟路由 @@ -46,6 +48,18 @@ const MainNavigator = createStackNavigator({ header: null } }, + WebViewPage: { + screen: WebViewPage, + navigationOptions: { + header: null + } + }, + AboutPage: { + screen: AboutPage, + navigationOptions: { + header: null + } + }, AxiosDemoPage: { screen: AxiosDemoPage, navigationOptions: { diff --git a/js/page/Mypage.js b/js/page/Mypage.js index 4256212..51fe5e1 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -7,9 +7,9 @@ import Ionicons from 'react-native-vector-icons/Ionicons'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; import { MORE_MENU } from '../common/MORE_MENU'; -import GobalStyles from '../res/GobalStyles'; +import GobalStyles from '../res/styles/GobalStyles'; import ViewUtil from '../util/ViewUtil'; - +import NavigationUtil from '../navigator/NavigationUtil'; const THEME_COLOR = '#f33' type Props = {}; @@ -19,7 +19,22 @@ type Props = {}; ) export default class My extends Component { onClick (menu) { - + let RouteName, params = {}; + switch (menu) { + case MORE_MENU.Tutorial: + RouteName = 'WebViewPage'; + params.title = '教程'; + params.url = 'https://ml.66jingcai.cn/' + break; + case MORE_MENU.About: + RouteName = 'AboutPage'; + break; + default: + break; + } + if (RouteName) { + NavigationUtil.goPage(params, RouteName); + } } getItem (menu) { return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); diff --git a/js/page/WebViewPage.js b/js/page/WebViewPage.js new file mode 100644 index 0000000..35c944e --- /dev/null +++ b/js/page/WebViewPage.js @@ -0,0 +1,93 @@ +import React, {Component} from 'react'; +import {StyleSheet,DeviceInfo, WebView, TouchableOpacity, Text, View} from 'react-native'; +import FontAwesome from 'react-native-vector-icons/FontAwesome'; +import NavigationUtil from '../navigator/NavigationUtil'; +import BackPressComponent from '../common/BackPressComponent'; +import FavoriteDao from '../expand/dao/FavoriteDao'; +// 自定义顶部导航组件 +import NavigationBar from '../common/NavigationBar'; +import ViewUtil from '../util/ViewUtil'; + +const THEME_COLOR='#f33'; +const TRENDING_URL="https://github.com/" +type Props = {}; +export default class WebViewPage extends Component { + constructor (props) { + super(props); + this.params = this.props.navigation.state.params; + const {title, url} = this.params; + this.state = { + title: title, + url: url, + canGoBack: false + } + this.backPress = new BackPressComponent({backPress: () => this.onBackPress()}); + } + componentDidMount () { + // 注册物理返回键的监听 + this.backPress.componentDidMount(); + } + componentWillUnmount() { + // 注销物理返回键的监听 + this.backPress.componentWillUnmount(); + } + onBackPress = () => { + /** + *处理android中的物理返回键 + *@returns{boolean} + * + */ + this.onBack(); + return true; + } + onBack () { + if(this.state.canGoBack) { + // 使用webView的goback返回webview中上一个路由 + this.webView.goBack() + } else { + // 否则返回我们native的上一页 + NavigationUtil.goBack({navigation:this.props.navigation}); + } + } + onNavigationStateChange (navState) { + // webview路由变化时调用 + this.setState({ + canGoBack: navState.canGoBack, + url: navState.url + }) + } + render() { + // 顶部导航栏设置 + // 状态栏设置 + // let statusBar = { + // backgroundColor: '#36c', + // barStyle: 'light-content', + // hidden: false + // }; + let navigationBar = this.onBackPress())} + /> + return ( + + {navigationBar} + this.webView = webView} + // 显示加载进度条 + startInLoadingState={true} + onNavigationStateChange={(e) => {this.onNavigationStateChange(e)}} + source={{uri:this.state.url}} + /> + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginTop: DeviceInfo.isIphoneX_deprecated?30:0, + } +}); diff --git a/js/page/about/AboutCommon.js b/js/page/about/AboutCommon.js new file mode 100644 index 0000000..effb9aa --- /dev/null +++ b/js/page/about/AboutCommon.js @@ -0,0 +1,188 @@ +import React from 'react'; +import {View, Image, Text, Dimensions, Platform,StatusBar, StyleSheet} from 'react-native'; +import BackPressComponent from '../../common/BackPressComponent'; +import NavigationUtil from '../../navigator/NavigationUtil'; +import config from '../../res/data/github_app_config.json'; +import ParallaxScrollView from 'react-native-parallax-scroll-view'; +import GobalStyles from '../../res/styles/GobalStyles'; +import ViewUtil from '../../util/ViewUtil'; +const THEME_COLOR="#F33"; +export const FLAG_ABOUT = { + flag_about: 'about', + flag_about_me: 'about_me' +} +export default class AboutCommon { + constructor(props, updateState) { + this.props = props; + this.updateState = updateState; + this.backPress = new BackPressComponent({backPress: () => this.onBackPress()}); + } + componentDidMount () { + // 注册物理返回键的监听 + this.backPress.componentDidMount(); + // 获取配置文件 + fetch('https://m.ethansblogs.com/github_app_config.json').then(response => { + if (response.ok) { + return response.json(); + } + throw new Error('NetWork Error') + }).then(config => { + if (config) { + this.updateState({ + data: config + }) + } + }).catch(e => { + console(e); + }) + } + componentWillUnmount() { + // 注销物理返回键的监听 + this.backPress.componentWillUnmount(); + } + onBackPress = () => { + /** + *处理android中的物理返回键 + */ + NavigationUtil.goBack({navigation:this.props.navigation}); + return true; + } + onShare () { + + } + getParallaxRenderConfig (params) { + let config = {}; + let avatar = typeof(params.avatar) === 'string' ? {uri: params.avatar} : params.avatar; + console.log(params) + // debugger + config.renderBackground=() => ( + // 背景设置 + + + + + ); + config.renderForeground=() => ( + // 前景 + + + + {params.name} + + + {params.description} + + + ); + + config.renderStickyHeader=() => ( + // 悬停的顶部展示内容 + + {params.name} + + ); + + config.renderFixedHeader=() => ( + // 顶部固定位置的左右按钮 + + {ViewUtil.getLeftBackButton(() => NavigationUtil.goBack({navigation: this.props.navigation}))} + {ViewUtil.getShareButton(() =>this.onShare())} + + ); + + return config; + } + render (contentView, params) { + const renderConfig = this.getParallaxRenderConfig(params); + return ( + + {/* 内容区域 */} + {contentView} + + ) + } +} + +const window = Dimensions.get('window'); +const AVATAR_SIZE = 90; +const PARALLAX_HEADER_HEIGHT = 270; +const STICKY_HEADER_HEIGHT = (Platform.OS === 'ios') ? GobalStyles.nav_bar_height_ios + 20 : GobalStyles.nav_bar_height_android; + +const styles = StyleSheet.create({ + container: { + // flex: 1, + backgroundColor: 'black' + }, + background: { + position: 'absolute', + top: 0, + left: 0, + width: window.width, + height: PARALLAX_HEADER_HEIGHT + }, + stickySection: { + height: STICKY_HEADER_HEIGHT, + // width: 300, + justifyContent: 'center' + }, + stickySectionText: { + color: 'white', + fontSize: 20, + margin: 10, + textAlign: 'center' + }, + fixedSection: { + position: 'absolute', + left:0, + right:0, + bottom:0, + paddingRight: 8, + paddingTop: (Platform.OS === 'ios')?20:0, + flexDirection: 'row', + alignItems: 'center', + justifyContent:'space-between' + }, + fixedSectionText: { + color: '#999', + fontSize: 20 + }, + parallaxHeader: { + alignItems: 'center', + flex: 1, + flexDirection: 'column', + paddingTop: 100 + }, + avatar: { + marginBottom: 10, + borderRadius: AVATAR_SIZE / 2 + }, + sectionSpeakerText: { + color: 'white', + fontSize: 24, + marginBottom: 10, + }, + sectionTitleText: { + color: 'white', + fontSize: 18, + marginRight: 10, + marginLeft: 10, + } +}); diff --git a/js/page/about/AboutPage.js b/js/page/about/AboutPage.js new file mode 100644 index 0000000..c01c7ce --- /dev/null +++ b/js/page/about/AboutPage.js @@ -0,0 +1,69 @@ +import React, {Component} from 'react'; +import { View, StatusBar, Linking } from 'react-native'; +import { MORE_MENU } from '../../common/MORE_MENU'; +import GobalStyles from '../../res/styles/GobalStyles'; +import ViewUtil from '../../util/ViewUtil'; +import NavigationUtil from '../../navigator/NavigationUtil'; +import AboutCommon, {FLAG_ABOUT} from './AboutCommon'; +import config from '../../res/data/github_app_config.json'; + +const THEME_COLOR = '#f33' +type Props = {}; +export default class AboutPage extends Component { + constructor (props) { + super(props); + this.params = this.props.navigation.state.params; + // debugger + this.aboutCommon = new AboutCommon({ + ...this.params, + navigation: this.props.navigation, + flagAbout: FLAG_ABOUT.flag_about_me, + }, data => this.setState({...data})) + this.state = { + data: config + } + } + onClick (menu) { + let RouteName, params = {}; + switch (menu) { + case MORE_MENU.Tutorial: + RouteName = 'WebViewPage'; + params.title = '教程'; + params.url = 'https://ml.66jingcai.cn/' + break; + case MORE_MENU.Feedback: + const url = "qqmail://172529131@qq.com" + Linking.canOpenURL(url).then(support => { + if (!support) { + console.log('Can\'t handle url:' + url); + } else { + Linking.openURL(url); + } + }).catch(e => { + console.error('An eooro occurred' + e); + }); + break; + default: + break; + } + if (RouteName) { + NavigationUtil.goPage(params, RouteName); + } + } + getItem (menu) { + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); + } + render() { + const content = + {/* 教程 */} + {this.getItem(MORE_MENU.Tutorial)} + + {/* 关于作者 */} + {this.getItem(MORE_MENU.About_Author)} + + {/* 反馈 */} + {this.getItem(MORE_MENU.Feedback)} + + return this.aboutCommon.render(content, this.state.data.app); + } +} diff --git a/js/res/data/github_app_config.json b/js/res/data/github_app_config.json new file mode 100644 index 0000000..000dcb0 --- /dev/null +++ b/js/res/data/github_app_config.json @@ -0,0 +1,90 @@ +{ + "aboutMe": { + "Tutorial": { + "name": "教程", + "icon": "ios-bookmarks", + "items": [ + { + "title": "React Native基础教程", + "url": "https://reactnative.cn/docs/sectionlist.html#docsNav" + }, + { + "title": "React Native高级实战教程", + "url": "https://coding.m.imooc.com/classindex.html?cid=89" + } + ] + }, + "Blog": { + "name": "技术博客", + "icon": "ios-laptop", + "items": [ + { + "title": "个人博客", + "url": "http://www.cnblogs.com/songdongdong/" + }, + { + "title": "CSDN", + "url": "http://www.cnblogs.com/songdongdong/" + }, + { + "title": "简书", + "url": "http://www.cnblogs.com/songdongdong/" + }, + { + "title": "GitHub", + "url": "https://github.com/songdongdong123" + }, + { + "title": "慕课网", + "url": "https://github.com/songdongdong123" + } + ] + }, + "Contact": { + "name": "联系方式", + "icon": "ios-contacts", + "items": { + "QQ": { + "title": "QQ", + "account": "172529131" + }, + "Email": { + "title": "Email", + "account": "ethanSongs@163.com" + } + } + }, + "QQ": { + "name": "技术交流群", + "icon": "ios-chatbubbles", + "items": [ + { + "title": "移动开发者技术分享群", + "account": "172529131" + }, + { + "title": "React Native学习交流群", + "account": "172529131" + } + ] + } + }, + "info": { + "html_url": "https://github.com/crazycodeboy/", + "url": "https://api.github.com/repos/crazycodeboy/", + "currentRepoUrl": "https://api.github.com/repos/crazycodeboy/GitHubPopular" + }, + "author": { + "name": "Ethan", + "description": "专注于前端开发,分享知识,共享快乐。", + "avatar": "https://avatars0.githubusercontent.com/u/20263883?s=400&u=221df9487b574ebfb83d3402d61a7839316b2c3d&v=4", + "backgroundImg": "http://www.devio.org/io/GitHubPopular/img/for_githubpopular_about_me.jpg", + "url": "https://github.com/songdongdong123" + }, + "app": { + "name": "GitHub Popular", + "description": "这是一个用来查看GitHub最受欢迎与最热项目的App,它基于React Native支持Android和iOS双平台。", + "avatar": "http://www.devio.org/io/GitHubPopular/img/ic_app.png", + "backgroundImg": "http://www.devio.org/io/GitHubPopular/img/for_githubpopular_about_me.jpg" + } +} \ No newline at end of file diff --git a/js/res/GobalStyles.js b/js/res/styles/GobalStyles.js similarity index 75% rename from js/res/GobalStyles.js rename to js/res/styles/GobalStyles.js index 332df9e..600bb00 100644 --- a/js/res/GobalStyles.js +++ b/js/res/styles/GobalStyles.js @@ -14,5 +14,8 @@ export default { root_container: { flex: 1, backgroundColor: BACKGROUND_COLOR - } + }, + nav_bar_height_ios: 44, + nav_bar_height_android: 50, + backgroundColor: BACKGROUND_COLOR }; \ No newline at end of file diff --git a/package.json b/package.json index 6d9a867..d16d3c4 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react-native-event-bus": "^1.0.0", "react-native-gesture-handler": "^1.1.0", "react-native-htmlview": "^0.13.0", + "react-native-parallax-scroll-view": "^0.21.3", "react-native-vector-icons": "^6.3.0", "react-navigation": "^3.3.2", "react-navigation-redux-helpers": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 2ac9d7d..863db10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4819,7 +4819,7 @@ prompts@^2.0.1: kleur "^3.0.2" sisteransi "^1.0.0" -prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "http://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU= @@ -4961,6 +4961,13 @@ react-native-htmlview@^0.13.0: entities "^1.1.1" htmlparser2-without-node-native "^3.9.2" +react-native-parallax-scroll-view@^0.21.3: + version "0.21.3" + resolved "http://registry.npm.taobao.org/react-native-parallax-scroll-view/download/react-native-parallax-scroll-view-0.21.3.tgz#d956ad224ce3b0a2725b80574bcbf688bbeda14e" + integrity sha1-2VatIkzjsKJyW4BXS8v2iLvtoU4= + dependencies: + prop-types "^15.6.0" + react-native-safe-area-view@^0.13.0: version "0.13.1" resolved "http://registry.npm.taobao.org/react-native-safe-area-view/download/react-native-safe-area-view-0.13.1.tgz#834bbb6d22f76a7ff07de56725ee5667ba1386b0" From b955e820671cb2327dfccf9416625554af2987b2 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Mon, 1 Apr 2019 16:06:29 +0800 Subject: [PATCH 03/20] Assembler mode --- js/navigator/AppNavigator.js | 7 +++ js/page/Mypage.js | 3 + js/page/about/AboutCommon.js | 3 +- js/page/about/AboutMePage.js | 114 +++++++++++++++++++++++++++++++++++ js/page/about/AboutPage.js | 5 +- 5 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 js/page/about/AboutMePage.js diff --git a/js/navigator/AppNavigator.js b/js/navigator/AppNavigator.js index 19c2d7e..37dcb4f 100644 --- a/js/navigator/AppNavigator.js +++ b/js/navigator/AppNavigator.js @@ -20,6 +20,7 @@ import AsyncStorageDemo from '../page/AsyncStorageDemo'; //AsyncStorage测试页 import DataStorePage from '../page/DataStorePage'; import WebViewPage from '../page/WebViewPage'; //浏览器页面 import AboutPage from '../page/about/AboutPage'; +import AboutMePage from '../page/about/AboutMePage' export const rootCom = 'Init'; //设置跟路由 @@ -60,6 +61,12 @@ const MainNavigator = createStackNavigator({ header: null } }, + AboutMePage: { + screen: AboutMePage, + navigationOptions: { + header: null + } + }, AxiosDemoPage: { screen: AxiosDemoPage, navigationOptions: { diff --git a/js/page/Mypage.js b/js/page/Mypage.js index 51fe5e1..fa17a2e 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -29,6 +29,9 @@ export default class My extends Component { case MORE_MENU.About: RouteName = 'AboutPage'; break; + case MORE_MENU.About_Author: + RouteName = 'AboutMePage'; + break; default: break; } diff --git a/js/page/about/AboutCommon.js b/js/page/about/AboutCommon.js index effb9aa..f8d8309 100644 --- a/js/page/about/AboutCommon.js +++ b/js/page/about/AboutCommon.js @@ -1,5 +1,5 @@ import React from 'react'; -import {View, Image, Text, Dimensions, Platform,StatusBar, StyleSheet} from 'react-native'; +import {View, Image, Text, Dimensions, Platform, StyleSheet} from 'react-native'; import BackPressComponent from '../../common/BackPressComponent'; import NavigationUtil from '../../navigator/NavigationUtil'; import config from '../../res/data/github_app_config.json'; @@ -53,7 +53,6 @@ export default class AboutCommon { getParallaxRenderConfig (params) { let config = {}; let avatar = typeof(params.avatar) === 'string' ? {uri: params.avatar} : params.avatar; - console.log(params) // debugger config.renderBackground=() => ( // 背景设置 diff --git a/js/page/about/AboutMePage.js b/js/page/about/AboutMePage.js new file mode 100644 index 0000000..7cbb191 --- /dev/null +++ b/js/page/about/AboutMePage.js @@ -0,0 +1,114 @@ +import React, {Component} from 'react'; +import { View, StatusBar, Linking, Clipboard } from 'react-native'; +import { MORE_MENU } from '../../common/MORE_MENU'; +import GobalStyles from '../../res/styles/GobalStyles'; +import ViewUtil from '../../util/ViewUtil'; +import NavigationUtil from '../../navigator/NavigationUtil'; +import AboutCommon, {FLAG_ABOUT} from './AboutCommon'; +import config from '../../res/data/github_app_config.json'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import Toast from 'react-native-easy-toast'; + +const THEME_COLOR = '#f33'; +type Props = {}; +export default class AboutMePage extends Component { + constructor (props) { + super(props); + this.params = this.props.navigation.state.params; + // debugger + this.aboutCommon = new AboutCommon({ + ...this.params, + navigation: this.props.navigation, + flagAbout: FLAG_ABOUT.flag_about_me, + }, data => this.setState({...data})) + this.state = { + data: config, + showTutorial: true, + showBlog: false, + showQQ: false, + showContact: false + } + } + onClick (tab) { + if (!tab) return; + if (tab.url) { + NavigationUtil.goPage({ + title: tab.title, + url: tab.url + }, 'WebViewPage') + return; + } + if (tab.account&&tab.account.indexOf('@')>-1) { + let url = 'qqmail://' + tab.account; + Linking.canOpenURL(url).then(support => { + if (!support) { + console.log('Can\'t handle url:' + url); + } else { + Linking.openURL(url); + } + }).catch(e => { + console.error('An eooro occurred' + e); + }); + return; + } + if (tab.account) { + Clipboard.setString(tab.account); + this.toast.show(tab.title + tab.account + '已复制到剪贴板。'); + } + } + getItem (menu) { + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); + } + _item (data, isShow, key) { + // console.log(this.state.data) + // debugger + return ViewUtil.getSettingItem(() => { + // 该箭头函数为,传递给getSettingItem中TouchableOpacity组件的onPress回调函数, + this.setState({ + [key]: !this.state[key] + }) + }, data.name, THEME_COLOR, Ionicons, data.icon, isShow?'ios-arrow-up': 'ios-arrow-down') + } + renderItems (dic, isShowAccount) { + if (!dic) return null; + let views = []; + for (let i in dic) { + let title = isShowAccount ? dic[i].title + ':' + dic[i].account : dic[i].title; + views.push( + + { + ViewUtil.getSettingItem(()=> this.onClick(dic[i]), title, THEME_COLOR) + } + + + ) + } + return views; + } + render() { + const content = + {this._item(this.state.data.aboutMe.Tutorial, this.state.showTutorial, 'showTutorial')} + + {this.state.showTutorial?this.renderItems(this.state.data.aboutMe.Tutorial.items):null} + + {this._item(this.state.data.aboutMe.Blog, this.state.showBlog, 'showBlog')} + + {this.state.showBlog?this.renderItems(this.state.data.aboutMe.Blog.items):null} + + {this._item(this.state.data.aboutMe.QQ, this.state.showQQ, 'showQQ')} + + {this.state.showQQ?this.renderItems(this.state.data.aboutMe.QQ.items):null} + + {this._item(this.state.data.aboutMe.Contact, this.state.showContact, 'showContact')} + + {this.state.showContact?this.renderItems(this.state.data.aboutMe.Contact.items):null} + + + return + {this.aboutCommon.render(content, this.state.data.author)} + this.toast=toast} + position={'center'} + /> + + } +} diff --git a/js/page/about/AboutPage.js b/js/page/about/AboutPage.js index c01c7ce..6268cd5 100644 --- a/js/page/about/AboutPage.js +++ b/js/page/about/AboutPage.js @@ -17,7 +17,7 @@ export default class AboutPage extends Component { this.aboutCommon = new AboutCommon({ ...this.params, navigation: this.props.navigation, - flagAbout: FLAG_ABOUT.flag_about_me, + flagAbout: FLAG_ABOUT.flag_about, }, data => this.setState({...data})) this.state = { data: config @@ -31,6 +31,9 @@ export default class AboutPage extends Component { params.title = '教程'; params.url = 'https://ml.66jingcai.cn/' break; + case MORE_MENU.About_Author: + RouteName = 'AboutMePage'; + break; case MORE_MENU.Feedback: const url = "qqmail://172529131@qq.com" Linking.canOpenURL(url).then(support => { From 989598ef7e670d8f2826d652ecf6a6e41b19fe4e Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 2 Apr 2019 18:26:22 +0800 Subject: [PATCH 04/20] languageDao --- js/expand/dao/LanguageDao.js | 41 ++++ js/page/PopularPage.js | 45 ++-- js/page/TrendingPage.js | 38 +++- js/redux/action/action_types.js | 1 + js/redux/action/index.js | 4 +- js/redux/action/language/index.js | 23 +++ js/redux/reducer/index.js | 4 +- js/redux/reducer/language/index.js | 35 ++++ js/res/data/keys.json | 38 ++++ js/res/data/langs.json | 319 +++++++++++++++++++++++++++++ js/util/ArrayUtil.js | 11 + 11 files changed, 533 insertions(+), 26 deletions(-) create mode 100644 js/expand/dao/LanguageDao.js create mode 100644 js/redux/action/language/index.js create mode 100644 js/redux/reducer/language/index.js create mode 100644 js/res/data/keys.json create mode 100644 js/res/data/langs.json create mode 100644 js/util/ArrayUtil.js diff --git a/js/expand/dao/LanguageDao.js b/js/expand/dao/LanguageDao.js new file mode 100644 index 0000000..47ee111 --- /dev/null +++ b/js/expand/dao/LanguageDao.js @@ -0,0 +1,41 @@ +import { AsyncStorage } from 'react-native'; +import keys from '../../res/data/keys.json'; +import langs from '../../res/data/langs.json'; +export const FLAG_LANGUAGE = { + flag_language: 'language_dao_language', + flag_key: 'language_dao_key' +}; +export default class LanguageDao { + constructor (flag) { + this.flag = flag; + } + fetch () { + // 获取语言或者标签 + return new Promise((resolve, reject) => { + AsyncStorage.getItem(this.flag, (error, result) => { + if (error) { + reject(error); + return; + } + if (!result) { + let data = this.flag === FLAG_LANGUAGE.flag_language ? langs : keys; + this.save(data); + resolve(data); + } else { + try { + resolve(JSON.parse(result)); + } catch (error) { + reject(error); + } + } + }); + }); + } + save (objectData) { + // 保存语言或标签 + let stringData = JSON.stringify(objectData); + AsyncStorage.setItem(this.flag, stringData, (error, result) => { + + }) + } +} \ No newline at end of file diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index 4fbf7c6..1561300 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -28,6 +28,9 @@ import NavigationBar from '../common/NavigationBar'; import EventTypes from '../util/EventTypes'; import EventBus from 'react-native-event-bus'; +// +import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; + // 顶部导航tab标签配置 const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; const URL = 'https://api.github.com/search/repositories?q='; @@ -37,29 +40,44 @@ const PAEG_SIZE = 10; const favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_popular); type Props = {}; -export default class Popuilar extends Component { +@connect( + state=>state.language, + { + onLoadLanguage: actions.onLoadLanguage + } +) +export default class Popular extends Component { constructor (props) { super(props); + const {onLoadLanguage} = this.props; + // 获取标签数据 + onLoadLanguage(FLAG_LANGUAGE.flag_key); } componentDidMount() { } _genTabs () { const tabs = {}; - TAB_NAMES.forEach((item, index) => { - tabs[`tab${index}`] = { - // 这里使用的箭头函数,所以直接使用props,而不是使用this.props - screen: props => , - // screen: function () { //如果是普通函数,则使用this.props - // return - // }, - navigationOptions: { - title: item + const {keys} = this.props; + keys.forEach((item, index) => { + if (item.checked) { + tabs[`tab${index}`] = { + // 这里使用的箭头函数,所以直接使用props,而不是使用this.props + screen: props => , + // screen: function () { //如果是普通函数,则使用this.props + // return + // }, + navigationOptions: { + title: item.name + } } } }); return tabs; } render() { + // 获取标签 + const {keys} = this.props; + console.log(this.props) // 状态栏设置 let statusBar = { backgroundColor: THEME_COLOR, @@ -72,7 +90,7 @@ export default class Popuilar extends Component { style={{backgroundColor: THEME_COLOR}} /> - const TabNavigator = createAppContainer(createMaterialTopTabNavigator( + const TabNavigator = keys.length ? createAppContainer(createMaterialTopTabNavigator( this._genTabs(), { // tabBar配置选项 tabBarOptions: { @@ -89,10 +107,11 @@ export default class Popuilar extends Component { labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) } } - )); + )):null; return {navigationBar} - + {/* TabNavigator存在,渲染标签 */} + {TabNavigator&&} } } diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index d23fa85..4405369 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -41,6 +41,8 @@ import TrendingDialog, {TimeSpans} from '../common/TrendingDialog'; import EventTypes from '../util/EventTypes'; import EventBus from 'react-native-event-bus'; +import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; + // 顶部导航tab标签配置 const TAB_NAMES = ['All', 'C', 'C#', 'PHP', 'Javascript']; const URL = 'https://github.com/trending/'; @@ -51,27 +53,40 @@ const EVENT_TYPE_TIME_SPAN_CHANGE = 'EVENT_TYPE_TIME_SPAN_CHANGE'; const favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_trending); type Props = {}; +@connect( + state=>state.language, + { + onLoadLanguage: actions.onLoadLanguage + } +) export default class Trending extends Component { constructor (props) { super(props); this.state = { timeSpan: TimeSpans[0] } + const {onLoadLanguage} = this.props; + onLoadLanguage(FLAG_LANGUAGE.flag_language); + this.preKeys = []; } componentDidMount() { // console.log(this.props) } _genTabs () { const tabs = {}; - TAB_NAMES.forEach((item, index) => { - tabs[`tab${index}`] = { - // 这里使用的箭头函数,所以直接使用props,而不是使用this.props - screen: props => , - // screen: function () { //如果是普通函数,则使用this.props - // return - // }, - navigationOptions: { - title: item + const {keys} = this.props; + this.preKeys = keys; + keys.forEach((item, index) => { + if (item.checked) { + tabs[`tab${index}`] = { + // 这里使用的箭头函数,所以直接使用props,而不是使用this.props + screen: props => , + // screen: function () { //如果是普通函数,则使用this.props + // return + // }, + navigationOptions: { + title: item.name + } } } }); @@ -142,6 +157,7 @@ export default class Trending extends Component { return this.tabNav; } render() { + const {keys} = this.props; // 状态栏设置 let statusBar = { backgroundColor: THEME_COLOR, @@ -154,10 +170,10 @@ export default class Trending extends Component { style={{backgroundColor: THEME_COLOR}} /> // 顶部标签组件 - const TabNavigator = this._tabNav(); + const TabNavigator = keys.length?this._tabNav():null; return {navigationBar} - + {TabNavigator&&} {this.renderTrendingDialog()} } diff --git a/js/redux/action/action_types.js b/js/redux/action/action_types.js index 380979c..92f87f9 100644 --- a/js/redux/action/action_types.js +++ b/js/redux/action/action_types.js @@ -16,4 +16,5 @@ export default { FAVORITE_LOAD_DATA: 'FAVORITE_LOAD_DATA', // 加载favorite页面数据 FAVORITE_LOAD_SUCCESS: 'FAVORITE_LOAD_SUCCESS', // favorite数据加载成功 FAVORITE_LOAD_FAIL: 'FAVORITE_LOAD_FAIL', + LANGUAGE_LOAD_SUCCESS: 'LANGUAGE_LOAD_SUCCESS' } \ No newline at end of file diff --git a/js/redux/action/index.js b/js/redux/action/index.js index 69388c3..59b451e 100644 --- a/js/redux/action/index.js +++ b/js/redux/action/index.js @@ -2,6 +2,7 @@ import {onThemeChange} from './theme'; import {onLoadPopularData, onLoadMorePopular, onFlushPopularFavorite} from './popular'; import {onLoadTrendingData, onLoadMoreTrending, onFlushTrendingFavorite} from './trending'; import {onLoadFavoriteData} from './favorite'; +import {onLoadLanguage} from './language'; export default { onThemeChange, @@ -11,5 +12,6 @@ export default { onLoadMoreTrending, onLoadFavoriteData, onFlushPopularFavorite, - onFlushTrendingFavorite + onFlushTrendingFavorite, + onLoadLanguage } \ No newline at end of file diff --git a/js/redux/action/language/index.js b/js/redux/action/language/index.js new file mode 100644 index 0000000..69be032 --- /dev/null +++ b/js/redux/action/language/index.js @@ -0,0 +1,23 @@ +import Types from '../action_types'; +import LanguageDao from '../../../expand/dao/LanguageDao'; +/* +* @name index +* @param {CLIPBOARD} +* @author Ethan +* @date 2019-03-26 19:30:08 +*/ +export function onLoadLanguage(flagkey) { + return async dispatch => { + // 派发刷新状态 + try { + let languages = await new LanguageDao(flagkey).fetch(); + dispatch({ + type: Types.LANGUAGE_LOAD_SUCCESS, + languages: languages, + flag: flagkey + }) + } catch (error) { + console.log(error) + } + } +} diff --git a/js/redux/reducer/index.js b/js/redux/reducer/index.js index 6278721..644aa2f 100644 --- a/js/redux/reducer/index.js +++ b/js/redux/reducer/index.js @@ -3,6 +3,7 @@ import theme from './theme'; import popular from './pouplar'; import trending from './trending'; import favorite from './favorite'; +import language from './language'; import { rootCom, RootNavigator } from '../../navigator/AppNavigator'; // 1.指定默认的state @@ -21,6 +22,7 @@ const index = combineReducers({ theme: theme, popular: popular, trending: trending, - favorite: favorite + favorite: favorite, + language: language }); export default index; \ No newline at end of file diff --git a/js/redux/reducer/language/index.js b/js/redux/reducer/language/index.js new file mode 100644 index 0000000..15e805f --- /dev/null +++ b/js/redux/reducer/language/index.js @@ -0,0 +1,35 @@ +import Types from '../../action/action_types'; +import {FLAG_LANGUAGE} from '../../../expand/dao/LanguageDao'; + +const defaultState = { + languages: [], + keys: [] +} + +/** + * + * + * @export + * @param {*} [state=defaultState] + * @param {*} action + * @returns + */ +export default function onAction (state=defaultState, action) { + switch (action.type) { + // 获取数据 + case Types.LANGUAGE_LOAD_SUCCESS: + if (FLAG_LANGUAGE.flag_key === action.flag) { + return { + ...state, + keys: action.languages + } + } else { + return { + ...state, + languages: action.languages + } + } + default: + return state; + } +} \ No newline at end of file diff --git a/js/res/data/keys.json b/js/res/data/keys.json new file mode 100644 index 0000000..bdf48c7 --- /dev/null +++ b/js/res/data/keys.json @@ -0,0 +1,38 @@ +[ + { + "path": "start:>1", + "name": "ALL", + "short_name": "ALL", + "checked": true + }, + { + "path": "ios", + "name": "ios", + "checked": true + }, + { + "path": "react-native", + "name": "React Native", + "checked": true + }, + { + "path": "MySQL", + "name": "MySQL", + "checked": false + }, + { + "path": "AngularJS", + "name": "AngularJS", + "checked": false + }, + { + "path": "jQuery", + "name": "jQuery", + "checked": false + }, + { + "path": "react", + "name": "React", + "checked": true + } +] \ No newline at end of file diff --git a/js/res/data/langs.json b/js/res/data/langs.json new file mode 100644 index 0000000..f3043e7 --- /dev/null +++ b/js/res/data/langs.json @@ -0,0 +1,319 @@ +[ + { + "path": "", + "name": "All Language", + "short_name": "All", + "checked": true + }, + { + "path": "unknown", + "name": "Unknown", + "checked": true + }, + { + "path": "as3", + "name": "ActionScript", + "short_name": "AS", + "checked": false + }, + { + "path": "apacheconf", + "name": "ApacheConf", + "checked": false + }, + { + "path": "nasm", + "name": "Assembly", + "short_name": "NASM", + "checked": false + }, + { + "path": "bat", + "name": "Batchfile", + "short_name": "BAT", + "checked": false + }, + { + "path": "c", + "name": "C", + "checked": false + }, + { + "path": "csharp", + "name": "C#", + "checked": false + }, + { + "path": "cpp", + "name": "C++", + "checked": false + }, + { + "path": "cmake", + "name": "CMake", + "checked": false + }, + { + "path": "css", + "name": "CSS", + "checked": false + }, + { + "path": "clojure", + "name": "Clojure", + "checked": false + }, + { + "path": "coffeescript", + "name": "CoffeeScript", + "checked": false + }, + { + "path": "common-lisp", + "short_name": "Lisp", + "name": "Common Lisp", + "checked": false + }, + { + "path": "crystal", + "name": "Crystal", + "checked": false + }, + { + "path": "d", + "name": "D", + "checked": false + }, + { + "path": "dart", + "name": "Dart", + "checked": false + }, + { + "path": "elixir", + "name": "Elixir", + "checked": false + }, + { + "path": "emacs-lisp", + "short_name": "Lisp", + "name": "Emacs Lisp", + "checked": false + }, + { + "path": "erlang", + "name": "Erlang", + "checked": false + }, + { + "path": "fsharp", + "name": "F#", + "checked": false + }, + { + "path": "game-maker-language", + "short_name": "GML", + "name": "Game Maker Language", + "checked": false + }, + { + "path": "go", + "name": "Go", + "checked": false + }, + { + "path": "groovy", + "name": "Groovy", + "checked": false + }, + { + "path": "html", + "name": "HTML", + "checked": false + }, + { + "path": "haskell", + "name": "Haskell", + "checked": false + }, + { + "path": "haxe", + "name": "Haxe", + "checked": false + }, + { + "path": "inno-setup", + "short_name": "Inno", + "name": "Inno Setup", + "checked": false + }, + { + "path": "java", + "name": "Java", + "checked": true + }, + { + "path": "javascript", + "short_name": "JS", + "name": "JavaScript", + "checked": true + }, + { + "path": "julia", + "name": "Julia", + "checked": false + }, + { + "path": "kotlin", + "name": "Kotlin", + "checked": true + }, + { + "path": "livescript", + "short_name": "LS", + "name": "LiveScript", + "checked": false + }, + { + "path": "lua", + "name": "Lua", + "checked": false + }, + { + "path": "makefile", + "name": "Makefile", + "checked": false + }, + { + "path": "matlab", + "name": "Matlab", + "checked": false + }, + { + "path": "nsis", + "name": "NSIS", + "checked": false + }, + { + "path": "nimrod", + "name": "Nimrod", + "checked": false + }, + { + "path": "ocaml", + "name": "OCaml", + "checked": false + }, + { + "short_name": "Obj-C", + "path": "objective-c", + "name": "Objective-C", + "checked": true + }, + { + "short_name": "Obj-C++", + "path": "objective-c%2B%2B", + "name": "Objective-C++", + "checked": false + }, + { + "path": "php", + "name": "PHP", + "checked": true + }, + { + "path": "plsql", + "name": "PLSQL", + "checked": false + }, + { + "path": "pascal", + "name": "Pascal", + "checked": false + }, + { + "path": "perl", + "name": "Perl", + "checked": false + }, + { + "path": "postscript", + "name": "PostScript", + "checked": false + }, + { + "path": "powershell", + "name": "PowerShell", + "checked": false + }, + { + "path": "python", + "name": "Python", + "checked": false + }, + { + "path": "qml", + "name": "QML", + "checked": false + }, + { + "path": "r", + "name": "R", + "checked": false + }, + { + "path": "ruby", + "name": "Ruby", + "checked": false + }, + { + "path": "rust", + "name": "Rust", + "checked": false + }, + { + "path": "scala", + "name": "Scala", + "checked": false + }, + { + "path": "scheme", + "name": "Scheme", + "checked": false + }, + { + "path": "bash", + "name": "Shell", + "checked": false + }, + { + "path": "supercollider", + "name": "SuperCollider", + "checked": false + }, + { + "path": "swift", + "name": "Swift", + "checked": false + }, + { + "path": "tex", + "name": "TeX", + "checked": false + }, + { + "path": "typescript", + "name": "TypeScript", + "checked": false + }, + { + "path": "verilog", + "name": "Verilog", + "checked": false + }, + { + "path": "xslt", + "name": "XSLT", + "checked": false + } +] \ No newline at end of file diff --git a/js/util/ArrayUtil.js b/js/util/ArrayUtil.js new file mode 100644 index 0000000..bc460d8 --- /dev/null +++ b/js/util/ArrayUtil.js @@ -0,0 +1,11 @@ +export default class ArrayUtil { + static isEqual (arr1, arr2) { + if (!(arr1&&arr2)) return false; // 如果有一个数组不存在或者为空,返回false + if (arr1.length !== arr2.length) return false; + for (let i = 0, l = arr1.length; i < l; i++) { + if (arr1[i] !== arr2[i]) return false; + } + return true; + + } +} \ No newline at end of file From 4e90bf377ac148f09cc0e18c90f12f5017a7471c Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Thu, 4 Apr 2019 18:03:29 +0800 Subject: [PATCH 05/20] theme --- js/expand/dao/ThemeDao.js | 35 +++++ js/expand/dao/dataStore.js | 4 +- js/navigator/AppNavigator.js | 16 ++- js/page/CustomKeyPage.js | 206 +++++++++++++++++++++++++++ js/page/CustomTheme.js | 120 ++++++++++++++++ js/page/Mypage.js | 26 +++- js/page/PopularPage.js | 3 +- js/page/SortKeyPage.js | 243 ++++++++++++++++++++++++++++++++ js/page/TrendingPage.js | 19 +-- js/redux/action/action_types.js | 1 + js/redux/action/index.js | 6 +- js/redux/action/theme/index.js | 30 +++- js/redux/reducer/theme/index.js | 20 ++- js/res/data/langs.json | 6 +- js/res/styles/ThemeFactory.js | 54 +++++++ js/util/ArrayUtil.js | 36 +++++ js/util/ViewUtil.js | 17 +++ package.json | 2 + yarn.lock | 12 ++ 19 files changed, 832 insertions(+), 24 deletions(-) create mode 100644 js/expand/dao/ThemeDao.js create mode 100644 js/page/CustomKeyPage.js create mode 100644 js/page/CustomTheme.js create mode 100644 js/page/SortKeyPage.js create mode 100644 js/res/styles/ThemeFactory.js diff --git a/js/expand/dao/ThemeDao.js b/js/expand/dao/ThemeDao.js new file mode 100644 index 0000000..c720d73 --- /dev/null +++ b/js/expand/dao/ThemeDao.js @@ -0,0 +1,35 @@ +import { AsyncStorage } from 'react-native'; +import ThemeFactory, { ThemeFlags } from '../../res/styles/ThemeFactory'; + +const THEME_KEY = 'theme_key' + +export default class ThemeDao { + + /** + * 获取当前主题 + * @returns {Promise} + */ + getTheme () { + return new Promise((resolve, reject) => { + AsyncStorage.getItem(THEME_KEY, (error, result) => { + if (error) { + reject(error) + return + } + if (!result) { + this.save(ThemeFlags.Default) + result = ThemeFlags.Default + } + resolve(ThemeFactory.createTheme(result)) + }) + }) + } + + /** + * 保存主题标识 + * @param themeFlag + */ + save (themeFlag) { + AsyncStorage.setItem(THEME_KEY, themeFlag, (error => {})) + } +} diff --git a/js/expand/dao/dataStore.js b/js/expand/dao/dataStore.js index bd7242a..b3eb102 100644 --- a/js/expand/dao/dataStore.js +++ b/js/expand/dao/dataStore.js @@ -19,14 +19,14 @@ export default class DataStore { resolve(wrapData); } else { this.fetchNetData(url, flag).then((data) => { - resolve(this.__wrapData(data)); + resolve(this._wrapData(data)); }).catch((error) => { reject(error); }) } }).catch((error) => { this.fetchNetData(url, flag).then((data) => { - resolve(this.__wrapData(data)); + resolve(this._wrapData(data)); }).catch((error) => { reject(error); }) diff --git a/js/navigator/AppNavigator.js b/js/navigator/AppNavigator.js index 37dcb4f..3767aba 100644 --- a/js/navigator/AppNavigator.js +++ b/js/navigator/AppNavigator.js @@ -20,7 +20,9 @@ import AsyncStorageDemo from '../page/AsyncStorageDemo'; //AsyncStorage测试页 import DataStorePage from '../page/DataStorePage'; import WebViewPage from '../page/WebViewPage'; //浏览器页面 import AboutPage from '../page/about/AboutPage'; -import AboutMePage from '../page/about/AboutMePage' +import AboutMePage from '../page/about/AboutMePage'; +import CustomKeyPage from '../page/CustomKeyPage'; +import SortKeyPage from '../page/SortKeyPage'; export const rootCom = 'Init'; //设置跟路由 @@ -55,6 +57,12 @@ const MainNavigator = createStackNavigator({ header: null } }, + CustomKeyPage: { + screen: CustomKeyPage, + navigationOptions: { + header: null + } + }, AboutPage: { screen: AboutPage, navigationOptions: { @@ -67,6 +75,12 @@ const MainNavigator = createStackNavigator({ header: null } }, + SortKeyPage: { + screen: SortKeyPage, + navigationOptions: { + header: null + } + }, AxiosDemoPage: { screen: AxiosDemoPage, navigationOptions: { diff --git a/js/page/CustomKeyPage.js b/js/page/CustomKeyPage.js new file mode 100644 index 0000000..8811d5f --- /dev/null +++ b/js/page/CustomKeyPage.js @@ -0,0 +1,206 @@ + +import React, {Component} from 'react'; +import {StyleSheet, TouchableOpacity, View, ScrollView, Text, Alert} from 'react-native'; +import Toast from 'react-native-easy-toast'; +import {connect} from 'react-redux'; +import actions from '../redux/action'; +import BackPressComponent from '../common/BackPressComponent'; +import CheckBox from 'react-native-check-box'; + +import { + createMaterialTopTabNavigator, + createAppContainer +} from 'react-navigation'; +import NavigationUtil from '../navigator/NavigationUtil'; +import FavoriteUtil from '../util/FavoriteUtil'; + +import NavigationBar from '../common/NavigationBar'; +import ViewUtil from '../util/ViewUtil'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import ArrayUtil from '../util/ArrayUtil'; + +// +import LanguageDao, {FLAG_LANGUAGE} from '../expand/dao/LanguageDao'; +const THEME_COLOR='#f33'; + +type Props = {}; + +@connect( + state=>state, + { + onLoadLanguage: actions.onLoadLanguage + } +) +export default class CustomKeyPage extends Component { + constructor (props) { + super(props); + this.params = this.props.navigation.state.params; + this.backPress = new BackPressComponent({backPress: (e) => this.onBackPress(e)}); + this.changeValues = []; + this.isRemoveKey = !!this.params.isRemoveKey; + this.languageDao = new LanguageDao(this.params.flag); + this.state = { + keys: [] + } + } + onBackPress = () => { + this.onBack(); + return true; + } + onBack () { + if (this.changeValues.length > 0) { + Alert.alert('提示','要保存修改吗', [ + { + text: '否', onPress: () => { + NavigationUtil.goBack({navigation:this.props.navigation}); + } + }, + { + text: '是', onPress: () => { + this.onSave(); + } + } + ]) + } else { + NavigationUtil.goBack({navigation:this.props.navigation}); + } + } + static _keys (props, original, state) { + const {flag, isRemoveKey} = props.navigation.state.params; + let key = flag === FLAG_LANGUAGE.flag_key?'keys':'languages'; + if (isRemoveKey&&!original) { + // 如果state中的keys为空则从props中获取 + return state && state.keys && state.keys.length !== 0 && state.keys || props.language[key].map(val => { + // 注意不直接修改props,copy一份 + return { + ...val, + checked: false + }; + }); + } else { + return props.language[key]; + } + } + static getDerivedStateFromProps (nextProps, prevState) { + if (prevState.keys !== CustomKeyPage._keys(nextProps, null, prevState)) { + return { + keys: CustomKeyPage._keys(nextProps, null, prevState) + } + } + return null; + } + componentDidMount() { + // 注册物理返回键的监听 + this.backPress.componentDidMount(); + // 如果props中标签为空,则从本地存储中获取标签 + if (CustomKeyPage._keys(this.props).length === 0) { + let {onLoadLanguage} = this.props; + onLoadLanguage(this.params.flag); + } + this.setState({ + keys: CustomKeyPage._keys(this.props) + }) + } + componentWillUnmount() { + // 注销物理返回键的监听 + this.backPress.componentWillUnmount(); + } + onSave () { + if (this.changeValues.length === 0) { + NavigationUtil.goBack({navigation:this.props.navigation}); + return; + } + let keys; + // 移除标签特殊处理 + if (this.isRemoveKey) { + for (let i = 0, l = this.changeValues.length; i < l; i++) { + ArrayUtil.remove(keys = CustomKeyPage._keys(this.props, true), this.changeValues[i], 'name'); + } + } + // 更新本地数据 + this.languageDao.save(keys || this.state.keys); + const {onLoadLanguage} = this.props; + // 更新store + onLoadLanguage(this.params.flag); + NavigationUtil.goBack({navigation:this.props.navigation}); + } + renderView () { + let dataArray = this.state.keys; + if (!dataArray || dataArray.length === 0) return; + let len = dataArray.length; + let views = []; + for (let i = 0, l = len; i < l; i += 2) { + views.push( + + + {this.renderCheckBox(dataArray[i], i)} + {i+1 < len && this.renderCheckBox(dataArray[i+1], i+1)} + + + + ) + } + return views; + } + onClick (data, index) { + data.checked = !data.checked; + ArrayUtil.updateArray(this.changeValues, data); + let tempkeys = this.state.keys; + tempkeys[index] = data; + this.setState({ + keys: tempkey + }) + } + _checkedImage (checked) { + const {theme} = this.props; + return + } + renderCheckBox (data, index) { + return this.onClick(data, index)} + isChecked={data.checked} + leftText={data.name} + checkedImage={this._checkedImage(true)} + unCheckedImage={this._checkedImage(false)} + /> + } + render() { + let title = this.isRemoveKey?'标签移除': '自定义标签'; + title = this.params.flag === FLAG_LANGUAGE.flag_language ? '自定义语言': title; + let rightButtonTitle = this.isRemoveKey?'移除':'保存'; + // 顶部导航栏设置 + let navigationBar = this.onBackPress())} + rightButton={ViewUtil.getRightButton(rightButtonTitle, () => this.onSave())} + /> + return + {navigationBar} + + {this.renderView()} + + + } +} +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#F5FCFF', + }, + item: { + flexDirection: 'row', + }, + line: { + flex: 1, + height: 0.3, + backgroundColor: 'darkgray' + } +}); diff --git a/js/page/CustomTheme.js b/js/page/CustomTheme.js new file mode 100644 index 0000000..dd3d386 --- /dev/null +++ b/js/page/CustomTheme.js @@ -0,0 +1,120 @@ +import React, { Component } from 'react' +import { + DeviceInfo, + Modal, + TouchableHighlight, + Platform, + ScrollView, + StyleSheet, + Text, + View +} from 'react-native' +import { connect } from 'react-redux' +import ThemeDao from '../expand/dao/ThemeDao'; +import ThemeFactory, { ThemeFlags } from '../res/styles/ThemeFactory'; +import GlobalStyles from '../res/styles/GobalStyles'; +import actions from '../redux/action'; + +@connect( + state=>state, + { + onThemeChange: actions.onThemeChange + } +) +export default class CustomKeyPage extends Component { + constructor (props) { + super(props) + this.themeDao = new ThemeDao() + } + + // 选择主题 + onSelectTheme (themeKey) { + const { onThemeChange } = this.props + this.props.onClose() + this.themeDao.saveTheme(ThemeFlags[themeKey]) + onThemeChange(ThemeFactory.createTheme(ThemeFlags[themeKey])) + } + + renderThemeItem = (themeKey) => { + return this.onSelectTheme(themeKey)} + > + + {themeKey} + + + } + + renderThemeItems = () => { + const views = [] + for (let i = 0, keys = Object.keys(ThemeFlags), length = keys.length; i < length; i += 3) { + const key1 = keys[i], key2 = keys[i + 1], key3 = keys[i + 2] + views.push( + + {this.renderThemeItem(key1)} + {this.renderThemeItem(key2)} + {this.renderThemeItem(key3)} + + ) + } + return views + } + + renderContentView = () => { + return ( + { + this.props.onClose() + }} + > + + + {this.renderThemeItems()} + + + + ) + } + + render () { + let view = this.props.visible ? + {this.renderContentView()} + : null + return view + } +} + +const styles = StyleSheet.create({ + themeItem: { + flex: 1, + height: 120, + margin: 3, + padding: 3, + borderRadius: 2, + alignItems: 'center', + justifyContent: 'center', + }, + modalContainer: { + flex: 1, + margin: 10, + marginBottom: 10 + (DeviceInfo.isIPhoneX_deprecated ? 24 : 0), + marginTop: Platform.OS === 'ios' ? 20 + (DeviceInfo.isIPhoneX_deprecated ? 24 : 0) : 10, + backgroundColor: 'white', + borderRadius: 3, + shadowColor: 'gray', + shadowOffset: { width: 2, height: 2 }, + shadowOpacity: 0.5, + shadowRadius: 2, + padding: 3 + }, + themeText: { + color: 'white', + fontWeight: '500', + fontSize: 16 + } +}) diff --git a/js/page/Mypage.js b/js/page/Mypage.js index fa17a2e..128631d 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -10,12 +10,16 @@ import { MORE_MENU } from '../common/MORE_MENU'; import GobalStyles from '../res/styles/GobalStyles'; import ViewUtil from '../util/ViewUtil'; import NavigationUtil from '../navigator/NavigationUtil'; +import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; const THEME_COLOR = '#f33' type Props = {}; @connect( state=>({}), - {onThemeChange: actions.onThemeChange} + { + onThemeChange: actions.onThemeChange, + onShowCustomThemeView: actions.onShowCustomThemeView + } ) export default class My extends Component { onClick (menu) { @@ -32,6 +36,26 @@ export default class My extends Component { case MORE_MENU.About_Author: RouteName = 'AboutMePage'; break; + case MORE_MENU.Sort_key: + RouteName = 'SortKeyPage'; + params.flag = FLAG_LANGUAGE.flag_key; + break; + case MORE_MENU.Sort_Language: + RouteName = 'SortKeyPage'; + params.flag = FLAG_LANGUAGE.flag_language; + break; + case MORE_MENU.Custom_Theme: + const {onShowCustomThemeView} = this.props; + onShowCustomThemeView(true); + console.log(0) + break; + case MORE_MENU.Custom_key: + case MORE_MENU.Custom_Language: + case MORE_MENU.Remove_key: + RouteName = 'CustomKeyPage'; + params.isRemoveKey = menu === MORE_MENU.Remove_key; + params.flag = menu !== MORE_MENU.Custom_Language ? FLAG_LANGUAGE.flag_key : FLAG_LANGUAGE.flag_language; + break; default: break; } diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index 1561300..bb79e71 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -105,7 +105,8 @@ export default class Popular extends Component { }, indicatorStyle: styles.indicatorStyle, //选项卡指示器的样式对象(选项卡底部的行) labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) - } + }, + lazy: true } )):null; return diff --git a/js/page/SortKeyPage.js b/js/page/SortKeyPage.js new file mode 100644 index 0000000..eaaa152 --- /dev/null +++ b/js/page/SortKeyPage.js @@ -0,0 +1,243 @@ + +import React, {Component} from 'react'; +import {StyleSheet, TouchableOpacity, View, Text, TouchableHighlight, Alert} from 'react-native'; +import {connect} from 'react-redux'; +import actions from '../redux/action'; +import BackPressComponent from '../common/BackPressComponent'; +import SortableListView from 'react-native-sortable-listview'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; + +import { + createMaterialTopTabNavigator, + createAppContainer +} from 'react-navigation'; +import NavigationUtil from '../navigator/NavigationUtil'; + +import NavigationBar from '../common/NavigationBar'; +import ViewUtil from '../util/ViewUtil'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import ArrayUtil from '../util/ArrayUtil'; + +// +import LanguageDao, {FLAG_LANGUAGE} from '../expand/dao/LanguageDao'; +const THEME_COLOR='#f33'; + +type Props = {}; + +@connect( + state=>state, + { + onLoadLanguage: actions.onLoadLanguage + } +) +export default class SortKeyPage extends Component { + constructor (props) { + super(props); + this.params = this.props.navigation.state.params; + this.backPress = new BackPressComponent({backPress: (e) => this.onBackPress(e)}); + this.languageDao = new LanguageDao(this.params.flag); + this.state = { + checkedArray: SortKeyPage._keys(this.props) + } + } + onBackPress = () => { + this.onBack(); + return true; + } + onBack () { + if (!ArrayUtil.isEqual(SortKeyPage._keys(this.props), this.state.checkedArray)) { + Alert.alert('提示', '要保存修改吗?', + [ + { + text: '否', onPress: () => { + NavigationUtil.goBack({navigation:this.props.navigation}) + } + }, { + text: '是', onPress: () => { + this.onSave(true) + } + } + ]) + } else { + NavigationUtil.goBack({navigation:this.props.navigation}) + } + } + static _keys (props, state) { + //如果state中有checkedArray则使用state中的checkedArray + // debugger + if (state && state.checkedArray && state.checkedArray.length) { + return state.checkedArray; + } + //否则从原始数据中获取checkedArray + const flag = SortKeyPage._flag(props); + let dataArray = props.language[flag] || []; + let keys = []; + for (let i = 0, j = dataArray.length; i < j; i++) { + let data = dataArray[i]; + if (data.checked) keys.push(data); + } + return keys; + } + static _flag (props) { + const { flag } = props.navigation.state.params; + return flag === FLAG_LANGUAGE.flag_key ? 'keys' : 'languages'; + } + static getDerivedStateFromProps (nextProps, prevState) { + const checkedArray = SortKeyPage._keys(nextProps, null, prevState) + if (prevState.keys !== checkedArray) { + return { + keys: checkedArray + } + } + return null; + } + componentDidMount() { + // 注册物理返回键的监听 + this.backPress.componentDidMount(); + // 如果props中标签为空,则从本地存储中获取标签 + if (SortKeyPage._keys(this.props).length === 0) { + let {onLoadLanguage} = this.props; + onLoadLanguage(this.params.flag); + } + } + componentWillUnmount() { + // 注销物理返回键的监听 + this.backPress.componentWillUnmount(); + } + onSave (hasChecked) { + if (!hasChecked) { + //如果没有排序则直接返回 + if (ArrayUtil.isEqual(SortKeyPage._keys(this.props), this.state.checkedArray)) { + NavigationUtil.goBack({navigation:this.props.navigation}) + return + } + } + //保存排序后的数据 + //获取排序后的数据 + //更新本地数据 + this.languageDao.save(this.getSortResult()) + //重新加载排序后的标签,以便其他页面能够及时更新 + const { onLoadLanguage } = this.props + //更新store + onLoadLanguage(this.params.flag) + NavigationUtil.goBack({navigation:this.props.navigation}) + } + getSortResult = () => { + const flag = SortKeyPage._flag(this.props) + //从原始数据中复制一份数据出来,以便对这份数据进行进行排序 + let sortResultArray = ArrayUtil.clone(this.props.language[flag]) + //获取排序之前的排列顺序 + const originalCheckedArray = SortKeyPage._keys(this.props) + //遍历排序之前的数据,用排序后的数据checkedArray进行替换 + for (let i = 0, length = originalCheckedArray.length; i < length; i++) { + let item = originalCheckedArray[i] + //找到要替换的元素所在位置 + let index = this.props.language[flag].indexOf(item) + //进行替换 + sortResultArray.splice(index, 1, this.state.checkedArray[i]) + } + return sortResultArray + } + + renderView () { + let dataArray = this.state.keys; + if (!dataArray || dataArray.length === 0) return; + let len = dataArray.length; + let views = []; + for (let i = 0, l = len; i < l; i += 2) { + views.push( + + + + + + ) + } + return views; + } + onClick (data, index) { + data.checked = !data.checked; + ArrayUtil.updateArray(this.changeValues, data); + let tempkeys = this.state.keys; + tempkeys[index] = data; + this.setState({ + keys: tempkeys + }) + } + _checkedImage (checked) { + const {theme} = this.props; + return + } + render() { + let title = this.params.flag === FLAG_LANGUAGE.flag_language ? '语言排序' : '标签排序' + // 顶部导航栏设置 + let navigationBar = this.onBackPress())} + rightButton={ViewUtil.getRightButton('保存', () => this.onSave())} + /> + return + {navigationBar} + { + this.state.checkedArray.splice(e.to, 0, this.state.checkedArray.splice(e.from, 1)[0]) + this.forceUpdate() + }} + renderRow={row => } + > + {this.renderView()} + + + } +} + +class SortCell extends Component { + render () { + console.log(this.props) + const { theme } = this.props + return + + + {this.props.data.name} + + + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + line: { + flex: 1, + height: 0.3, + backgroundColor: 'darkgray', + }, + hidden: { + height: 0 + }, + item: { + backgroundColor: '#F8F8F8', + borderBottomWidth: 1, + borderColor: '#eee', + height: 50, + justifyContent: 'center' + }, +}); diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index 4405369..3718940 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -42,6 +42,7 @@ import EventTypes from '../util/EventTypes'; import EventBus from 'react-native-event-bus'; import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; +import ArrayUtil from '../util/ArrayUtil'; // 顶部导航tab标签配置 const TAB_NAMES = ['All', 'C', 'C#', 'PHP', 'Javascript']; @@ -70,13 +71,13 @@ export default class Trending extends Component { this.preKeys = []; } componentDidMount() { - // console.log(this.props) + console.log(this.props) } _genTabs () { const tabs = {}; - const {keys} = this.props; - this.preKeys = keys; - keys.forEach((item, index) => { + const {languages} = this.props; + this.preKeys = languages; + languages.forEach((item, index) => { if (item.checked) { tabs[`tab${index}`] = { // 这里使用的箭头函数,所以直接使用props,而不是使用this.props @@ -134,7 +135,7 @@ export default class Trending extends Component { // 优化效率:根据需要选择是否重新创建TabNavigator // 通常tab发生变化,才需要重新创建 // TabNavigator - if (!this.tabNav) { + if (!this.tabNav || !ArrayUtil.isEqual(this.preKeys, this.props.key)) { this.tabNav = createAppContainer(createMaterialTopTabNavigator( this._genTabs(), { // tabBar配置选项 @@ -150,14 +151,16 @@ export default class Trending extends Component { }, indicatorStyle: styles.indicatorStyle, //选项卡指示器的样式对象(选项卡底部的行) labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) - } + }, + lazy: true } )); } return this.tabNav; } render() { - const {keys} = this.props; + const {languages} = this.props; + console.log(languages) // 状态栏设置 let statusBar = { backgroundColor: THEME_COLOR, @@ -170,7 +173,7 @@ export default class Trending extends Component { style={{backgroundColor: THEME_COLOR}} /> // 顶部标签组件 - const TabNavigator = keys.length?this._tabNav():null; + const TabNavigator = languages.length?this._tabNav():null; return {navigationBar} {TabNavigator&&} diff --git a/js/redux/action/action_types.js b/js/redux/action/action_types.js index 92f87f9..777236d 100644 --- a/js/redux/action/action_types.js +++ b/js/redux/action/action_types.js @@ -1,6 +1,7 @@ export default { THEME_CHANGE: 'THEME_CHANGE', THEME_INIT: 'THEME_INIT', + SHOW_THEME_VIEW: 'SHOW_THEME_VIEW', POPULAR_REFRESH: 'POPULAR_REFRESH', //popular数据刷新 POPULAR_REFRESH_FAIL: 'POPULAR_REFRESH_FAIL', //popular数据加载失败 POPULAR_REFRESH_SUCCESS: 'POPULAR_REFRESH_SUCCESS', //popular数据加载成功 diff --git a/js/redux/action/index.js b/js/redux/action/index.js index 59b451e..3d74a0a 100644 --- a/js/redux/action/index.js +++ b/js/redux/action/index.js @@ -1,4 +1,4 @@ -import {onThemeChange} from './theme'; +import {onThemeChange, onThemeInit, onShowCustomThemeView} from './theme'; import {onLoadPopularData, onLoadMorePopular, onFlushPopularFavorite} from './popular'; import {onLoadTrendingData, onLoadMoreTrending, onFlushTrendingFavorite} from './trending'; import {onLoadFavoriteData} from './favorite'; @@ -13,5 +13,7 @@ export default { onLoadFavoriteData, onFlushPopularFavorite, onFlushTrendingFavorite, - onLoadLanguage + onLoadLanguage, + onThemeInit, + onShowCustomThemeView } \ No newline at end of file diff --git a/js/redux/action/theme/index.js b/js/redux/action/theme/index.js index 9ff1c09..0ae73ec 100644 --- a/js/redux/action/theme/index.js +++ b/js/redux/action/theme/index.js @@ -1,8 +1,34 @@ import Types from '../action_types'; - +import ThemeDao from '../../../expand/dao/ThemeDao'; +/** + * 主题更改 + * @param theme + * @returns {{theme: *, type: string}} + */ export function onThemeChange(theme) { return { type: Types.THEME_CHANGE, theme: theme } -} \ No newline at end of file +} + +/** + * 初始化主题 + * @returns {Function} + */ +export function onThemeInit () { + return dispatch => { + new ThemeDao().getTheme().then(data => { + dispatch(onThemeChange(data)) + }) + } +} + +/** + * 显示自定义主题弹窗 + * @param show + * @returns {{type: string, customThemeVisible: *}} + */ +export function onShowCustomThemeView (show) { + return { type: Types.SHOW_THEME_VIEW, customThemeViewVisible: show } +} diff --git a/js/redux/reducer/theme/index.js b/js/redux/reducer/theme/index.js index bdcbd36..a7ab92b 100644 --- a/js/redux/reducer/theme/index.js +++ b/js/redux/reducer/theme/index.js @@ -1,13 +1,25 @@ import Types from '../../action/action_types'; +import ThemeFactory, { ThemeFlags } from '../../../res/styles/ThemeFactory'; const defaultState = { - theme: '#f33' + theme: ThemeFactory.createTheme(ThemeFlags.Default), + + customThemeViewVisible: false } -export default function onAction (state=defaultState, action) { + +export default function onAction (state = defaultState, action) { switch (action.type) { case Types.THEME_CHANGE: - return {...state, theme:action.theme}; + return { + ...state, + theme: action.theme + } + case Types.SHOW_THEME_VIEW: + return { + ...state, + customThemeViewVisible: action.customThemeViewVisible + } default: - return state; + return state } } \ No newline at end of file diff --git a/js/res/data/langs.json b/js/res/data/langs.json index f3043e7..0bbe606 100644 --- a/js/res/data/langs.json +++ b/js/res/data/langs.json @@ -294,7 +294,7 @@ { "path": "swift", "name": "Swift", - "checked": false + "checked": true }, { "path": "tex", @@ -304,12 +304,12 @@ { "path": "typescript", "name": "TypeScript", - "checked": false + "checked": true }, { "path": "verilog", "name": "Verilog", - "checked": false + "checked": true }, { "path": "xslt", diff --git a/js/res/styles/ThemeFactory.js b/js/res/styles/ThemeFactory.js new file mode 100644 index 0000000..3cd7047 --- /dev/null +++ b/js/res/styles/ThemeFactory.js @@ -0,0 +1,54 @@ +/** + * 主题 + */ +import React from 'react' +import { StyleSheet } from 'react-native' + +export const ThemeFlags = { + Default: '#2196F3', + Red: '#F44336', + Pink: '#E91E63', + Purple: '#9C27B0', + DeepPurple: '#673AB7', + Indigo: '#3F51B5', + Blue: '#2196F3', + LightBlue: '#03A9F4', + Cyan: '#00BCD4', + Teal: '#009688', + Green: '#4CAF50', + LightGreen: '#8BC34A', + Lime: '#CDDC39', + Yellow: '#FFEB3B', + Amber: '#FFC107', + Orange: '#FF9800', + DeepOrange: '#FF5722', + Brown: '#795548', + Grey: '#9E9E9E', + BlueGrey: '#607D8B', + Black: '#000000' +} + +export default class ThemeFactory { + /** + * 创建一个主题样式 + * @param themeFlag 主题标识 + * @returns {{themeColor: *, styles: *}} + */ + static createTheme (themeFlag) { + return { + themeColor: themeFlag, + styles: StyleSheet.create({ + selectedTitleStyle: { + color: themeFlag, + }, + tabBarSelectedIcon: { + tintColor: themeFlag, + }, + navBar: { + backgroundColor: themeFlag, + } + }), + } + + } +} diff --git a/js/util/ArrayUtil.js b/js/util/ArrayUtil.js index bc460d8..e1a19b4 100644 --- a/js/util/ArrayUtil.js +++ b/js/util/ArrayUtil.js @@ -6,6 +6,42 @@ export default class ArrayUtil { if (arr1[i] !== arr2[i]) return false; } return true; + } + static updateArray (array, item) { + // 更新数组 + for (let i = 0, len = array.length; i < len; i++) { + let temp = array[i]; + if (item === temp) { + array.splice(i, 1); + return; + } + } + array.push(item); + } + static remove (array, item, id) { + // 移除元素 + if (!array) return; + for (let i = 0, l = array.length; i < l; i++) { + const val = array[i]; + if (item === val || val && val[id] && val[id] === item[id]) { + array.splice(i, 1); + } + } + return array; + } + + /** + * 数组克隆 + * @param from + * @returns {Array} + */ + static clone (from) { + if (!from) return [] + let newArray = [] + for (let i = 0, length = from.length; i < length; i++) { + newArray[i] = from[i] + } + return newArray } } \ No newline at end of file diff --git a/js/util/ViewUtil.js b/js/util/ViewUtil.js index 451b145..4ad8e2a 100644 --- a/js/util/ViewUtil.js +++ b/js/util/ViewUtil.js @@ -85,6 +85,23 @@ export default class ViewUtil { static getMenuItem (callback, menu, color, expandableIcon) { return ViewUtil.getSettingItem(callback, menu.name, color, menu.Icons, menu.icon, expandableIcon) } + + /** + * 获取右侧文字按钮 + * @param title + * @param callBack + * @returns {*} + */ + static getRightButton (title, callBack) { + return + + {title} + + + } } const styles = StyleSheet.create({ diff --git a/package.json b/package.json index d16d3c4..04d3bc0 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,13 @@ "axios": "^0.18.0", "react": "16.6.3", "react-native": "0.58.6", + "react-native-check-box": "^2.1.7", "react-native-easy-toast": "^1.2.0", "react-native-event-bus": "^1.0.0", "react-native-gesture-handler": "^1.1.0", "react-native-htmlview": "^0.13.0", "react-native-parallax-scroll-view": "^0.21.3", + "react-native-sortable-listview": "^0.2.8", "react-native-vector-icons": "^6.3.0", "react-navigation": "^3.3.2", "react-navigation-redux-helpers": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 863db10..934d071 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4921,6 +4921,13 @@ react-lifecycles-compat@^3.0.4: resolved "http://registry.npm.taobao.org/react-lifecycles-compat/download/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha1-TxonOv38jzSIqMUWv9p4+HI1I2I= +react-native-check-box@^2.1.7: + version "2.1.7" + resolved "http://registry.npm.taobao.org/react-native-check-box/download/react-native-check-box-2.1.7.tgz#f349d3505a13c15d19949ec4cec40c7adc4f3626" + integrity sha1-80nTUFoTwV0ZlJ7EzsQMetxPNiY= + dependencies: + prop-types "^15.5.7" + react-native-easy-toast@^1.2.0: version "1.2.0" resolved "http://registry.npm.taobao.org/react-native-easy-toast/download/react-native-easy-toast-1.2.0.tgz#0f70bcb99e3306cda4800c244bfb4a67d42276ed" @@ -4980,6 +4987,11 @@ react-native-safe-area-view@^0.13.0: resolved "http://registry.npm.taobao.org/react-native-screens/download/react-native-screens-1.0.0-alpha.22.tgz#7a120377b52aa9bbb94d0b8541a014026be9289b" integrity sha1-ehIDd7Uqqbu5TQuFQaAUAmvpKJs= +react-native-sortable-listview@^0.2.8: + version "0.2.8" + resolved "http://registry.npm.taobao.org/react-native-sortable-listview/download/react-native-sortable-listview-0.2.8.tgz#0d514fa73eb406a46baea96aa38a71e40257af90" + integrity sha1-DVFPpz60BqRrrqlqo4px5AJXr5A= + react-native-tab-view@^1.0.0, react-native-tab-view@^1.2.0: version "1.3.2" resolved "http://registry.npm.taobao.org/react-native-tab-view/download/react-native-tab-view-1.3.2.tgz#c4e43a538dcacce151938745cea09176beeccbc3" From ad48fee79f54186b66bc2fe212fa986b32f78e9f Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Thu, 4 Apr 2019 21:55:10 +0800 Subject: [PATCH 06/20] themecolor --- js/common/BaseItem.js | 3 +- js/expand/dao/FavoriteDao.js | 1 - js/expand/dao/ThemeDao.js | 12 +++---- js/navigator/DynamicTabNavigator.js | 2 +- js/page/CustomKeyPage.js | 40 +++++++++++----------- js/page/CustomTheme.js | 2 +- js/page/DetailPage.js | 8 +++-- js/page/FavoritePage.js | 31 ++++++++++-------- js/page/Homepage.js | 28 ++++++++++++++-- js/page/Mypage.js | 23 +++++++------ js/page/PopularPage.js | 35 +++++++++++--------- js/page/SortKeyPage.js | 51 ++++++++++++++--------------- js/page/TrendingPage.js | 39 +++++++++++++--------- js/page/WebViewPage.js | 8 +++-- js/page/about/AboutCommon.js | 6 ++-- js/page/about/AboutMePage.js | 13 ++++---- js/page/about/AboutPage.js | 5 +-- js/redux/action/theme/index.js | 7 ++-- js/redux/reducer/theme/index.js | 8 ++--- js/res/styles/ThemeFactory.js | 2 +- 20 files changed, 184 insertions(+), 140 deletions(-) diff --git a/js/common/BaseItem.js b/js/common/BaseItem.js index 43e1ffc..72a1e26 100644 --- a/js/common/BaseItem.js +++ b/js/common/BaseItem.js @@ -52,6 +52,7 @@ export default class BaseItem extends Component { this.props.onFavorite(this.props.projectModel.item, !this.state.isFavorite); } _favoriteIcon () { + const {theme} = this.props; return } diff --git a/js/expand/dao/FavoriteDao.js b/js/expand/dao/FavoriteDao.js index cd56618..20c28f5 100644 --- a/js/expand/dao/FavoriteDao.js +++ b/js/expand/dao/FavoriteDao.js @@ -5,7 +5,6 @@ const FAVORITE_KEY_PREFIX = 'favorite_'; export default class FavoriteDao { constructor (flag) { this.favoriteKey = FAVORITE_KEY_PREFIX + flag; - console.log(this.favoriteKey) } /** * 收藏项目,保存收藏项目 diff --git a/js/expand/dao/ThemeDao.js b/js/expand/dao/ThemeDao.js index c720d73..20ea53c 100644 --- a/js/expand/dao/ThemeDao.js +++ b/js/expand/dao/ThemeDao.js @@ -13,14 +13,14 @@ export default class ThemeDao { return new Promise((resolve, reject) => { AsyncStorage.getItem(THEME_KEY, (error, result) => { if (error) { - reject(error) - return + reject(error); + return; } if (!result) { - this.save(ThemeFlags.Default) - result = ThemeFlags.Default + this.save(ThemeFlags.Default); + result = ThemeFlags.Default; } - resolve(ThemeFactory.createTheme(result)) + resolve(ThemeFactory.createTheme(result)); }) }) } @@ -30,6 +30,6 @@ export default class ThemeDao { * @param themeFlag */ save (themeFlag) { - AsyncStorage.setItem(THEME_KEY, themeFlag, (error => {})) + AsyncStorage.setItem(THEME_KEY, themeFlag, (error => {})); } } diff --git a/js/navigator/DynamicTabNavigator.js b/js/navigator/DynamicTabNavigator.js index 9529800..62cc5f4 100644 --- a/js/navigator/DynamicTabNavigator.js +++ b/js/navigator/DynamicTabNavigator.js @@ -99,7 +99,7 @@ export default class DynamicTabNavigator extends Component { PopularPage.navigationOptions.tabBarLabel = '最热' //动态配置Tab属性 return this.Tabs = createAppContainer(createBottomTabNavigator(tabs, { // //这里使用tabBarComponent来覆盖用作标签栏的组件. 达到动态修改标签栏的样式等 - tabBarComponent: props => + tabBarComponent: props => })); } render() { diff --git a/js/page/CustomKeyPage.js b/js/page/CustomKeyPage.js index 8811d5f..7f9cd2e 100644 --- a/js/page/CustomKeyPage.js +++ b/js/page/CustomKeyPage.js @@ -21,7 +21,6 @@ import ArrayUtil from '../util/ArrayUtil'; // import LanguageDao, {FLAG_LANGUAGE} from '../expand/dao/LanguageDao'; -const THEME_COLOR='#f33'; type Props = {}; @@ -43,6 +42,22 @@ export default class CustomKeyPage extends Component { keys: [] } } + componentDidMount() { + // 注册物理返回键的监听 + this.backPress.componentDidMount(); + // 如果props中标签为空,则从本地存储中获取标签 + if (CustomKeyPage._keys(this.props).length === 0) { + let {onLoadLanguage} = this.props; + onLoadLanguage(this.params.flag); + } + this.setState({ + keys: CustomKeyPage._keys(this.props) + }) + } + componentWillUnmount() { + // 注销物理返回键的监听 + this.backPress.componentWillUnmount(); + } onBackPress = () => { this.onBack(); return true; @@ -89,22 +104,6 @@ export default class CustomKeyPage extends Component { } return null; } - componentDidMount() { - // 注册物理返回键的监听 - this.backPress.componentDidMount(); - // 如果props中标签为空,则从本地存储中获取标签 - if (CustomKeyPage._keys(this.props).length === 0) { - let {onLoadLanguage} = this.props; - onLoadLanguage(this.params.flag); - } - this.setState({ - keys: CustomKeyPage._keys(this.props) - }) - } - componentWillUnmount() { - // 注销物理返回键的监听 - this.backPress.componentWillUnmount(); - } onSave () { if (this.changeValues.length === 0) { NavigationUtil.goBack({navigation:this.props.navigation}); @@ -148,11 +147,11 @@ export default class CustomKeyPage extends Component { let tempkeys = this.state.keys; tempkeys[index] = data; this.setState({ - keys: tempkey + keys: tempkeys }) } _checkedImage (checked) { - const {theme} = this.props; + const {theme} = this.params; return { title = this.params.flag === FLAG_LANGUAGE.flag_language ? '自定义语言': title; let rightButtonTitle = this.isRemoveKey?'移除':'保存'; // 顶部导航栏设置 + const {theme} = this.params; let navigationBar = this.onBackPress())} rightButton={ViewUtil.getRightButton(rightButtonTitle, () => this.onSave())} /> diff --git a/js/page/CustomTheme.js b/js/page/CustomTheme.js index dd3d386..a47ff0d 100644 --- a/js/page/CustomTheme.js +++ b/js/page/CustomTheme.js @@ -31,7 +31,7 @@ export default class CustomKeyPage extends Component { onSelectTheme (themeKey) { const { onThemeChange } = this.props this.props.onClose() - this.themeDao.saveTheme(ThemeFlags[themeKey]) + this.themeDao.save(ThemeFlags[themeKey]) onThemeChange(ThemeFactory.createTheme(ThemeFlags[themeKey])) } diff --git a/js/page/DetailPage.js b/js/page/DetailPage.js index f263098..5f19e28 100644 --- a/js/page/DetailPage.js +++ b/js/page/DetailPage.js @@ -7,10 +7,13 @@ import FavoriteDao from '../expand/dao/FavoriteDao'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; import ViewUtil from '../util/ViewUtil'; +import {connect} from 'react-redux'; -const THEME_COLOR='#f33'; const TRENDING_URL="https://github.com/" type Props = {}; +@connect( + state=>state.theme +) export default class Detail extends Component { constructor (props) { super(props); @@ -95,12 +98,13 @@ export default class Detail extends Component { } render() { // 顶部导航栏设置 + const {theme} = this.props; const titleLayoutStyle = this.state.title.length > 20 ?{paddingRight: 30,} : null; let navigationBar = this.onBack())} titleLayoutStyle={titleLayoutStyle} title={this.state.title} - style={{backgroundColor: THEME_COLOR}} + style={{backgroundColor: theme.themeColor}} rightButton={this.renderRightButton()} /> return ( diff --git a/js/page/FavoritePage.js b/js/page/FavoritePage.js index a9c390f..26f6b98 100644 --- a/js/page/FavoritePage.js +++ b/js/page/FavoritePage.js @@ -30,9 +30,11 @@ import EventBus from 'react-native-event-bus'; // 顶部导航tab标签配置 const TAB_NAMES = ['最热', '趋势']; -const THEME_COLOR='#f33'; type Props = {}; +@connect( + state=>state.theme +) export default class Favorite extends Component { constructor (props) { super(props); @@ -40,16 +42,17 @@ export default class Favorite extends Component { componentDidMount() { } render() { + const {theme} = this.props; // 状态栏设置 let statusBar = { - backgroundColor: THEME_COLOR, + backgroundColor: theme.themeColor, barStyle: 'light-content', }; // 顶部导航栏设置 let navigationBar = const TabNavigator = createAppContainer(createMaterialTopTabNavigator({ @@ -68,8 +71,8 @@ export default class Favorite extends Component { }, { // tabBar配置选项 tabBarOptions: { - inactiveTintColor: '#333', - activeTintColor: '#f33', + inactiveTintColor: theme.themeColor, + activeTintColor: theme.themeColor, tabStyle: styles.tabStyle, //选项卡的样式对象 upperCaseLabel: false, //是否使标签大写,默认为 true。 scrollEnabled: false, // 是否支持 选项卡滚动 默认为 false @@ -77,7 +80,10 @@ export default class Favorite extends Component { backgroundColor: '#fff', height: 35 // fix 开启scrollEnabled后在android上初次渲染的时候会有高度闪烁的问题,所以这里需要固定高度 }, - indicatorStyle: styles.indicatorStyle, //选项卡指示器的样式对象(选项卡底部的行) + indicatorStyle: { + height: 2, + backgroundColor: theme.themeColor + }, //选项卡指示器的样式对象(选项卡底部的行) labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) } } @@ -144,8 +150,10 @@ class FavoriteTab extends Component { renderItem (data) { const item = data.item; const Item = this.storeName === FLAG_STORAGE.flag_popular ? PopularItem : TrendingItem; + const {theme} = this.props.theme; return { NavigationUtil.goPage({ projectModel: item, @@ -167,6 +175,7 @@ class FavoriteTab extends Component { } render() { let store = this._store(); // 动态获取state + const {theme} = this.props.theme; return ( { refreshControl={ this.loadData(true)} - tintColor={THEME_COLOR} + tintColor={theme.themeColor} /> } /> @@ -202,10 +211,6 @@ const styles = StyleSheet.create({ // width: 100, padding:0 }, - indicatorStyle: { - height: 2, - backgroundColor: '#f33' - }, labelStyle: { fontSize: 13, }, diff --git a/js/page/Homepage.js b/js/page/Homepage.js index 1a68d6e..402f471 100644 --- a/js/page/Homepage.js +++ b/js/page/Homepage.js @@ -1,16 +1,25 @@ import React, {Component} from 'react'; +import {View} from 'react-native'; import NavigationUtil from '../navigator/NavigationUtil'; import DynamicTabNavigator from '../navigator/DynamicTabNavigator'; import BackPressComponent from '../common/BackPressComponent'; +import CustomTheme from '../page/CustomTheme'; // 处理安卓的物理返回键 import {BackHandler} from 'react-native'; import {NavigationActions} from 'react-navigation'; import {connect} from 'react-redux'; +import actions from '../redux/action'; type Props = {}; @connect( - state=>state + state=>({ + nav: state.nav, + customThemeViewVisible: state.theme.customThemeViewVisible + }), + { + onShowCustomThemeView: actions.onShowCustomThemeView + } ) export default class Home extends Component { constructor (props) { @@ -39,10 +48,25 @@ export default class Home extends Component { dispatch(NavigationActions.back()); return true; } + renderCustomThemeView () { + const {onShowCustomThemeView, customThemeViewVisible } = this.props; + return ( + onShowCustomThemeView(false)} + /> + ); + } render() { // 内层的navigator往外层的navigator跳转的时候无法跳转, // 这个时候可以在内层保存外层的navigation NavigationUtil.navigation = this.props.navigation; // 给导航管理类添加静态属性 - return + return ( + + {this.renderCustomThemeView()} + + + ) } } diff --git a/js/page/Mypage.js b/js/page/Mypage.js index 128631d..35f18f4 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -11,19 +11,17 @@ import GobalStyles from '../res/styles/GobalStyles'; import ViewUtil from '../util/ViewUtil'; import NavigationUtil from '../navigator/NavigationUtil'; import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; - -const THEME_COLOR = '#f33' type Props = {}; @connect( - state=>({}), + state=>state.theme, { - onThemeChange: actions.onThemeChange, onShowCustomThemeView: actions.onShowCustomThemeView } ) export default class My extends Component { onClick (menu) { - let RouteName, params = {}; + const { theme } = this.props + let RouteName, params = {theme}; switch (menu) { case MORE_MENU.Tutorial: RouteName = 'WebViewPage'; @@ -47,7 +45,6 @@ export default class My extends Component { case MORE_MENU.Custom_Theme: const {onShowCustomThemeView} = this.props; onShowCustomThemeView(true); - console.log(0) break; case MORE_MENU.Custom_key: case MORE_MENU.Custom_Language: @@ -64,17 +61,19 @@ export default class My extends Component { } } getItem (menu) { - return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); + const {theme} = this.props; + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, theme.themeColor); } render() { + const {theme} = this.props; let statusBar = { - backgroundColor: THEME_COLOR, + backgroundColor: theme.themeColor, barStyle: 'light-content' }; let navigationBar = return ( @@ -90,10 +89,10 @@ export default class My extends Component { size={40} style={{ marginRight: 10, - color: THEME_COLOR + color: theme.themeColor }} /> - GitHub Popular + GitHub Populars { style={{ marginRight: 10, alignSelf: 'center', - color: THEME_COLOR + color: theme.themeColor }} /> diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index bb79e71..01021d7 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -35,13 +35,15 @@ import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; const URL = 'https://api.github.com/search/repositories?q='; const QUERY_STR = '&sort=stars'; -const THEME_COLOR='#f33'; const PAEG_SIZE = 10; const favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_popular); type Props = {}; @connect( - state=>state.language, + state=>({ + keys: state.language.keys, + theme: state.theme.theme + }), { onLoadLanguage: actions.onLoadLanguage } @@ -76,26 +78,25 @@ export default class Popular extends Component { } render() { // 获取标签 - const {keys} = this.props; - console.log(this.props) + const {keys, theme} = this.props; // 状态栏设置 let statusBar = { - backgroundColor: THEME_COLOR, + backgroundColor: theme.themeColor, barStyle: 'light-content', }; // 顶部导航栏设置 let navigationBar = const TabNavigator = keys.length ? createAppContainer(createMaterialTopTabNavigator( this._genTabs(), { // tabBar配置选项 tabBarOptions: { - inactiveTintColor: '#333', - activeTintColor: '#f33', + inactiveTintColor: theme.themeColor, + activeTintColor: theme.themeColor, tabStyle: styles.tabStyle, //选项卡的样式对象 upperCaseLabel: false, //是否使标签大写,默认为 true。 scrollEnabled: true, // 是否支持 选项卡滚动 默认为 false @@ -103,7 +104,10 @@ export default class Popular extends Component { backgroundColor: '#fff', height: 35 // fix 开启scrollEnabled后在android上初次渲染的时候会有高度闪烁的问题,所以这里需要固定高度 }, - indicatorStyle: styles.indicatorStyle, //选项卡指示器的样式对象(选项卡底部的行) + indicatorStyle: { + height: 2, + backgroundColor: theme.themeColor + }, //选项卡指示器的样式对象(选项卡底部的行) labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) }, lazy: true @@ -188,8 +192,10 @@ class PopuilarTab extends Component { } renderItem (data) { const item = data.item; + const {theme} = this.props.theme return { NavigationUtil.goPage({ projectModel: item, @@ -211,6 +217,7 @@ class PopuilarTab extends Component { } render() { let store = this._store(); // 动态获取state + const {theme} = this.props.theme return ( { refreshControl={ this.loadData()} - tintColor={THEME_COLOR} + tintColor={theme.themeColor} /> } ListFooterComponent={() => this.genIndicator()} @@ -264,10 +271,6 @@ const styles = StyleSheet.create({ // width: 100, padding:0 }, - indicatorStyle: { - height: 2, - backgroundColor: '#f33' - }, labelStyle: { fontSize: 13, // margin: 0, diff --git a/js/page/SortKeyPage.js b/js/page/SortKeyPage.js index eaaa152..9bca917 100644 --- a/js/page/SortKeyPage.js +++ b/js/page/SortKeyPage.js @@ -32,10 +32,12 @@ type Props = {}; ) export default class SortKeyPage extends Component { constructor (props) { - super(props); + super(props) this.params = this.props.navigation.state.params; - this.backPress = new BackPressComponent({backPress: (e) => this.onBackPress(e)}); + console.log(this.params) + this.backPress = new BackPressComponent({ backPress: (e) => this.onBackPress(e) }); this.languageDao = new LanguageDao(this.params.flag); + this.state = { checkedArray: SortKeyPage._keys(this.props) } @@ -50,7 +52,7 @@ export default class SortKeyPage extends Component { [ { text: '否', onPress: () => { - NavigationUtil.goBack({navigation:this.props.navigation}) + NavigationUtil.goBack({navigation: this.props.navigation}) } }, { text: '是', onPress: () => { @@ -59,24 +61,23 @@ export default class SortKeyPage extends Component { } ]) } else { - NavigationUtil.goBack({navigation:this.props.navigation}) + NavigationUtil.goBack({navigation: this.props.navigation}) } } static _keys (props, state) { //如果state中有checkedArray则使用state中的checkedArray - // debugger if (state && state.checkedArray && state.checkedArray.length) { - return state.checkedArray; + return state.checkedArray } //否则从原始数据中获取checkedArray - const flag = SortKeyPage._flag(props); - let dataArray = props.language[flag] || []; - let keys = []; + const flag = SortKeyPage._flag(props) + let dataArray = props.language[flag] || [] + let keys = [] for (let i = 0, j = dataArray.length; i < j; i++) { - let data = dataArray[i]; - if (data.checked) keys.push(data); + let data = dataArray[i] + if (data.checked) keys.push(data) } - return keys; + return keys } static _flag (props) { const { flag } = props.navigation.state.params; @@ -84,20 +85,19 @@ export default class SortKeyPage extends Component { } static getDerivedStateFromProps (nextProps, prevState) { const checkedArray = SortKeyPage._keys(nextProps, null, prevState) - if (prevState.keys !== checkedArray) { + if (prevState.checkedArray.length !== checkedArray.length) { return { - keys: checkedArray + checkedArray, } } - return null; + return null } componentDidMount() { - // 注册物理返回键的监听 - this.backPress.componentDidMount(); - // 如果props中标签为空,则从本地存储中获取标签 + this.backPress.componentDidMount() + //如果props中标签为空则从本地存储中获取标签 if (SortKeyPage._keys(this.props).length === 0) { - let {onLoadLanguage} = this.props; - onLoadLanguage(this.params.flag); + const { onLoadLanguage } = this.props + onLoadLanguage(this.params.flag) } } componentWillUnmount() { @@ -158,10 +158,9 @@ export default class SortKeyPage extends Component { onClick (data, index) { data.checked = !data.checked; ArrayUtil.updateArray(this.changeValues, data); - let tempkeys = this.state.keys; - tempkeys[index] = data; + this.state.keys[index] = data; this.setState({ - keys: tempkeys + keys: this.state.keys }) } _checkedImage (checked) { @@ -177,9 +176,10 @@ export default class SortKeyPage extends Component { render() { let title = this.params.flag === FLAG_LANGUAGE.flag_language ? '语言排序' : '标签排序' // 顶部导航栏设置 + const { theme } = this.props.theme; let navigationBar = this.onBackPress())} rightButton={ViewUtil.getRightButton('保存', () => this.onSave())} /> @@ -203,7 +203,6 @@ export default class SortKeyPage extends Component { class SortCell extends Component { render () { - console.log(this.props) const { theme } = this.props return + style={{ marginRight: 10, color: theme.themeColor }}/> {this.props.data.name} diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index 3718940..4a18009 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -44,18 +44,18 @@ import EventBus from 'react-native-event-bus'; import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; import ArrayUtil from '../util/ArrayUtil'; -// 顶部导航tab标签配置 -const TAB_NAMES = ['All', 'C', 'C#', 'PHP', 'Javascript']; const URL = 'https://github.com/trending/'; const QUERY_STR = '&sort=stars'; -const THEME_COLOR='#f33'; const PAEG_SIZE = 10; const EVENT_TYPE_TIME_SPAN_CHANGE = 'EVENT_TYPE_TIME_SPAN_CHANGE'; const favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_trending); type Props = {}; @connect( - state=>state.language, + state=>({ + languages: state.language.languages, + theme: state.theme.theme + }), { onLoadLanguage: actions.onLoadLanguage } @@ -71,7 +71,7 @@ export default class Trending extends Component { this.preKeys = []; } componentDidMount() { - console.log(this.props) + } _genTabs () { const tabs = {}; @@ -135,13 +135,15 @@ export default class Trending extends Component { // 优化效率:根据需要选择是否重新创建TabNavigator // 通常tab发生变化,才需要重新创建 // TabNavigator - if (!this.tabNav || !ArrayUtil.isEqual(this.preKeys, this.props.key)) { + const {theme} = this.props; + if (theme !== this.theme || !this.tabNav || !ArrayUtil.isEqual(this.preKeys, this.props.key)) { + this.theme = theme this.tabNav = createAppContainer(createMaterialTopTabNavigator( this._genTabs(), { // tabBar配置选项 tabBarOptions: { - inactiveTintColor: '#333', - activeTintColor: '#f33', + inactiveTintColor: theme.themeColor, + activeTintColor: theme.themeColor, tabStyle: styles.tabStyle, //选项卡的样式对象 upperCaseLabel: false, //是否使标签大写,默认为 true。 scrollEnabled: true, // 是否支持 选项卡滚动 默认为 false @@ -149,7 +151,10 @@ export default class Trending extends Component { backgroundColor: '#fff', //fix 开启scrollEnabled后在android上初次渲染的时候会有高度闪烁的问题,所以这里需要固定高度 height:35 }, - indicatorStyle: styles.indicatorStyle, //选项卡指示器的样式对象(选项卡底部的行) + indicatorStyle: { + height: 2, + backgroundColor: theme.themeColor + }, //选项卡指示器的样式对象(选项卡底部的行) labelStyle: styles.labelStyle, // 选项卡标签的样式对象(选项卡文字样式,颜色字体大小等) }, lazy: true @@ -159,18 +164,17 @@ export default class Trending extends Component { return this.tabNav; } render() { - const {languages} = this.props; - console.log(languages) + const {languages, theme} = this.props; // 状态栏设置 let statusBar = { - backgroundColor: THEME_COLOR, + backgroundColor: theme.themeColor, barStyle: 'light-content' }; // 顶部导航栏设置 let navigationBar = // 顶部标签组件 const TabNavigator = languages.length?this._tabNav():null; @@ -261,8 +265,10 @@ class TrendingTab extends Component { } renderItem (data) { const item = data.item; + const {theme} = this.props.theme; return { NavigationUtil.goPage({ projectModel: item, @@ -284,6 +290,7 @@ class TrendingTab extends Component { } render() { let store = this._store(); // 动态获取state + const {theme} = this.props.theme return ( { refreshControl={ this.loadData()} - tintColor={THEME_COLOR} + tintColor={theme.themeColor} /> } ListFooterComponent={() => this.genIndicator()} diff --git a/js/page/WebViewPage.js b/js/page/WebViewPage.js index 35c944e..0c2fbfb 100644 --- a/js/page/WebViewPage.js +++ b/js/page/WebViewPage.js @@ -7,10 +7,13 @@ import FavoriteDao from '../expand/dao/FavoriteDao'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; import ViewUtil from '../util/ViewUtil'; +import {connect} from 'react-redux'; -const THEME_COLOR='#f33'; const TRENDING_URL="https://github.com/" type Props = {}; +@connect( + state=>state.theme +) export default class WebViewPage extends Component { constructor (props) { super(props); @@ -64,10 +67,11 @@ export default class WebViewPage extends Component { // barStyle: 'light-content', // hidden: false // }; + const {theme} = this.props; let navigationBar = this.onBackPress())} /> return ( diff --git a/js/page/about/AboutCommon.js b/js/page/about/AboutCommon.js index f8d8309..b228b78 100644 --- a/js/page/about/AboutCommon.js +++ b/js/page/about/AboutCommon.js @@ -6,11 +6,12 @@ import config from '../../res/data/github_app_config.json'; import ParallaxScrollView from 'react-native-parallax-scroll-view'; import GobalStyles from '../../res/styles/GobalStyles'; import ViewUtil from '../../util/ViewUtil'; -const THEME_COLOR="#F33"; + export const FLAG_ABOUT = { flag_about: 'about', flag_about_me: 'about_me' } + export default class AboutCommon { constructor(props, updateState) { this.props = props; @@ -105,9 +106,10 @@ export default class AboutCommon { } render (contentView, params) { const renderConfig = this.getParallaxRenderConfig(params); + const {theme} = this.props; return ( { constructor (props) { super(props); this.params = this.props.navigation.state.params; - // debugger this.aboutCommon = new AboutCommon({ ...this.params, navigation: this.props.navigation, @@ -57,19 +55,22 @@ export default class AboutMePage extends Component { } } getItem (menu) { - return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); + const {theme} = this.params; + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, theme.themeColor); } _item (data, isShow, key) { // console.log(this.state.data) // debugger + const {theme} = this.params; return ViewUtil.getSettingItem(() => { // 该箭头函数为,传递给getSettingItem中TouchableOpacity组件的onPress回调函数, this.setState({ [key]: !this.state[key] }) - }, data.name, THEME_COLOR, Ionicons, data.icon, isShow?'ios-arrow-up': 'ios-arrow-down') + }, data.name, theme.themeColor, Ionicons, data.icon, isShow?'ios-arrow-up': 'ios-arrow-down') } renderItems (dic, isShowAccount) { + const {theme} = this.params; if (!dic) return null; let views = []; for (let i in dic) { @@ -77,7 +78,7 @@ export default class AboutMePage extends Component { views.push( { - ViewUtil.getSettingItem(()=> this.onClick(dic[i]), title, THEME_COLOR) + ViewUtil.getSettingItem(()=> this.onClick(dic[i]), title, theme.themeColor) } diff --git a/js/page/about/AboutPage.js b/js/page/about/AboutPage.js index 6268cd5..fa796ba 100644 --- a/js/page/about/AboutPage.js +++ b/js/page/about/AboutPage.js @@ -7,8 +7,8 @@ import NavigationUtil from '../../navigator/NavigationUtil'; import AboutCommon, {FLAG_ABOUT} from './AboutCommon'; import config from '../../res/data/github_app_config.json'; -const THEME_COLOR = '#f33' type Props = {}; + export default class AboutPage extends Component { constructor (props) { super(props); @@ -54,7 +54,8 @@ export default class AboutPage extends Component { } } getItem (menu) { - return ViewUtil.getMenuItem(() => this.onClick(menu), menu, THEME_COLOR); + const {theme} = this.params; + return ViewUtil.getMenuItem(() => this.onClick(menu), menu, theme.themeColor); } render() { const content = diff --git a/js/redux/action/theme/index.js b/js/redux/action/theme/index.js index 0ae73ec..6bfd5ef 100644 --- a/js/redux/action/theme/index.js +++ b/js/redux/action/theme/index.js @@ -5,11 +5,8 @@ import ThemeDao from '../../../expand/dao/ThemeDao'; * @param theme * @returns {{theme: *, type: string}} */ -export function onThemeChange(theme) { - return { - type: Types.THEME_CHANGE, - theme: theme - } +export function onThemeChange (theme) { + return { type: Types.THEME_CHANGE, theme } } /** diff --git a/js/redux/reducer/theme/index.js b/js/redux/reducer/theme/index.js index a7ab92b..eb7ec12 100644 --- a/js/redux/reducer/theme/index.js +++ b/js/redux/reducer/theme/index.js @@ -1,13 +1,11 @@ import Types from '../../action/action_types'; -import ThemeFactory, { ThemeFlags } from '../../../res/styles/ThemeFactory'; - +import ThemeFactory, {ThemeFlags} from '../../../res/styles/ThemeFactory'; +console.log(ThemeFactory.createTheme(ThemeFlags.Default)) const defaultState = { theme: ThemeFactory.createTheme(ThemeFlags.Default), - customThemeViewVisible: false } - -export default function onAction (state = defaultState, action) { +export default function onAction (state=defaultState, action) { switch (action.type) { case Types.THEME_CHANGE: return { diff --git a/js/res/styles/ThemeFactory.js b/js/res/styles/ThemeFactory.js index 3cd7047..0166f9c 100644 --- a/js/res/styles/ThemeFactory.js +++ b/js/res/styles/ThemeFactory.js @@ -5,7 +5,7 @@ import React from 'react' import { StyleSheet } from 'react-native' export const ThemeFlags = { - Default: '#2196F3', + Default: '#f33', Red: '#F44336', Pink: '#E91E63', Purple: '#9C27B0', From 064b485bc22f463f8a6e2dbd1e7ea4e543dc4b93 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Sat, 6 Apr 2019 17:10:15 +0800 Subject: [PATCH 07/20] Search --- js/navigator/AppNavigator.js | 7 + js/page/CustomTheme.js | 2 +- js/page/Mypage.js | 2 +- js/page/SearchPage.js | 313 +++++++++++++++++++++++++++++++ js/page/TrendingPage.js | 4 +- js/page/about/AboutPage.js | 3 +- js/redux/action/ActionUtil.js | 6 + js/redux/action/action_types.js | 13 +- js/redux/action/index.js | 6 +- js/redux/action/search/index.js | 135 +++++++++++++ js/redux/reducer/index.js | 4 +- js/redux/reducer/search/index.js | 62 ++++++ 12 files changed, 550 insertions(+), 7 deletions(-) create mode 100644 js/page/SearchPage.js create mode 100644 js/redux/action/search/index.js create mode 100644 js/redux/reducer/search/index.js diff --git a/js/navigator/AppNavigator.js b/js/navigator/AppNavigator.js index 3767aba..4b0cb5e 100644 --- a/js/navigator/AppNavigator.js +++ b/js/navigator/AppNavigator.js @@ -23,6 +23,7 @@ import AboutPage from '../page/about/AboutPage'; import AboutMePage from '../page/about/AboutMePage'; import CustomKeyPage from '../page/CustomKeyPage'; import SortKeyPage from '../page/SortKeyPage'; +import SearchPage from '../page/SearchPage'; export const rootCom = 'Init'; //设置跟路由 @@ -81,6 +82,12 @@ const MainNavigator = createStackNavigator({ header: null } }, + SearchPage: { + screen: SearchPage, + navigationOptions: { + header: null + } + }, AxiosDemoPage: { screen: AxiosDemoPage, navigationOptions: { diff --git a/js/page/CustomTheme.js b/js/page/CustomTheme.js index a47ff0d..7f0422d 100644 --- a/js/page/CustomTheme.js +++ b/js/page/CustomTheme.js @@ -82,7 +82,7 @@ export default class CustomKeyPage extends Component { } render () { - let view = this.props.visible ? + let view = this.props.visible ? {this.renderContentView()} : null return view diff --git a/js/page/Mypage.js b/js/page/Mypage.js index 35f18f4..e596709 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -73,7 +73,7 @@ export default class My extends Component { let navigationBar = return ( diff --git a/js/page/SearchPage.js b/js/page/SearchPage.js new file mode 100644 index 0000000..9ac40f5 --- /dev/null +++ b/js/page/SearchPage.js @@ -0,0 +1,313 @@ +import React, {Component} from 'react'; +import {StyleSheet, + DeviceInfo, + Platform, + ActivityIndicator, View, Text, FlatList, RefreshControl } from 'react-native'; +import Toast from 'react-native-easy-toast'; +import {connect} from 'react-redux'; +import actions from '../redux/action'; +import FavoriteDao from '../expand/dao/FavoriteDao'; +import {FLAG_STORAGE} from '../expand/dao/dataStore'; +import { + createMaterialTopTabNavigator, + createAppContainer +} from 'react-navigation'; +import NavigationUtil from '../navigator/NavigationUtil'; +import FavoriteUtil from '../util/FavoriteUtil'; +import PopularItem from '../common/PopularItem'; +import BackPressComponent from '../common/BackPressComponent'; +// 自定义顶部导航组件 +import NavigationBar from '../common/NavigationBar'; + +//用于页面之间通讯 +import EventTypes from '../util/EventTypes'; +import EventBus from 'react-native-event-bus'; + +// +import LanguageDao, { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; + +// 顶部导航tab标签配置 +const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; +const URL = 'https://api.github.com/search/repositories?q='; +const QUERY_STR = '&sort=stars'; +const PAEG_SIZE = 10; +type Props = {}; + +@connect( + state=>({ + keys: state.language.keys, + theme: state.theme.theme, + search: state.search, + }), + { + onSearch: onSearch, + onSearchCancel: actions.onSearchCancel, + onLoadMoreSearch: actions.onLoadMoreSearch, + onLoadLanguage: actions.onLoadLanguage + } +) +export default class Search extends Component { + constructor (props) { + super(props) + this.params = this.props.navigation.state.params + this.backPress = new BackPressComponent({ backPress: (e) => this.onBackPress(e) }) + this.favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_popular) + this.languageDao = new LanguageDao(FLAG_LANGUAGE.flag_key) + this.isKeyChange = false + } + componentDidMount () { + this.backPress.componentDidMount() + } + componentWillUnmount () { + this.backPress.componentWillUnmount() + } + // 加载数据 + loadData = (loadMore) => { + const { onLoadMoreSearch, onSearch, search, keys } = this.props + if (loadMore) { + onLoadMoreSearch(++search.pageIndex, pageSize, search.items, this.favoriteDao, keys, callback => { + this.toast.show('没有更多了') + }) + } else { + onSearch(this.inputKey, pageSize, this.searchToken = new Date().getTime(), this.favoriteDao, keys, message => { + this.toast.show(message) + }) + } + } + onBackPress = () => { + const { onSearchCancel, onLoadLanguage } = this.props + onSearchCancel()//退出时取消搜索 + this.refs.input.blur() + NavigationUtil.goBack(this.props.navigation) + if (this.isKeyChange) { + onLoadLanguage(FLAG_LANGUAGE.flag_key) + } + return true + } + //保存标签 + saveKey = () => { + const { keys } = this.props + let key = this.inputKey + if (Utils.checkKeyIsExist(keys, key)) { + this.toast.show(key + '已经存在') + } else { + key = { + path: key, + name: key, + checked: true + } + keys.unshift(key)//将key添加到数组的开头 + this.languageDao.save(keys) + this.toast.show(`${key.name}保存成功`) + this.isKeyChange = true + } + } + + onRightButtonClick = () => { + const { onSearchCancel, search } = this.props + if (search.showText === '搜索') { + this.loadData() + } else { + onSearchCancel(this.searchToken) + } + } + + renderItem = (data) => { + const item = data.item + const { theme } = this.params + return { + NavigationUtil.goPage({ + theme, + projectModel: item, + flag: FLAG_STORAGE.flag_popular, + callback, + }, 'DetailPage') + }} + onFavorite={(item, isFavorite) => FavoriteUtil.onFavorite(favoriteDao, item, isFavorite, FLAG_STORAGE.flag_popular)} + /> + } + // 加载条 + renderIndicator = () => { + const { search } = this.props + return search.hideLoadingMore ? null : + + + 正在加载更多 + + } + // 顶部导航 + renderNavBar = () => { + const { theme } = this.params + const { showText, inputKey } = this.props.search + const placeholder = inputKey || '请输入' + let backButton = ViewUtil.getLeftBackButton(() => this.onBackPress()) + let inputView = this.inputKey = text} + style={styles.textInput} + /> + let rightButton = { + this.refs.input.blur()//收起键盘 + this.onRightButtonClick() + }} + > + + {showText} + + + return + {backButton} + {inputView} + {rightButton} + + } + + render() { + const { theme } = this.params; + const { isLoading, projectModels, showBottomButton, hideLoadingMore} = this.props.search; + let statusBar = null + if (Platform.OS === 'ios') { + statusBar = + } + let listView = !isLoading ? this.renderItem(data)} + keyExtractor={item => '' + item.item.id} + contentInset={ //底部安全距离 + { + bottom: 45 + } + } + refreshControl={ + this.loadData()} + tintColor={theme.themeColor} + /> + } + ListFooterComponent={() => this.renderIndicator()} + onEndReached={() => { + console.log('---onEndReached----') + setTimeout(() => { + if (this.canLoadMore) {//fix 滚动时两次调用onEndReached https://github.com/facebook/react-native/issues/14015 + this.loadData(true) + this.canLoadMore = false + } + }, 100) + }} + onEndReachedThreshold={0.5} + onMomentumScrollBegin={() => { + this.canLoadMore = true //fix 初始化时页调用onEndReached的问题 + console.log('---onMomentumScrollBegin-----') + }} + /> : null + let bottomButton = showBottomButton ? + { + this.saveKey() + }} + > + + 朕收下了 + + : null + let indicatorView = isLoading ? + : null + let resultView = + {indicatorView} + {listView} + + return + {statusBar} + {this.renderNavBar()} + {resultView} + {bottomButton} + this.toast = toast}/> + + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + tabStyle: { + // minWidth: 50 //fix minWidth会导致tabStyle初次加载时闪烁 + padding: 0 + }, + indicatorStyle: { + height: 2, + backgroundColor: 'white' + }, + labelStyle: { + fontSize: 13, + margin: 0, + }, + indicatorContainer: { + alignItems: 'center' + }, + indicator: { + color: 'red', + margin: 10 + }, + statusBar: { + height: 20 + }, + bottomButton: { + alignItems: 'center', + justifyContent: 'center', + opacity: 0.9, + height: 40, + position: 'absolute', + left: 10, + top: GlobalStyles.window_height - 45 - (DeviceInfo.isIPhoneX_deprecated ? 34 : 0) - (Platform.OS === 'ios' ? 0 : 25), + right: 10, + borderRadius: 3 + }, + centering: { + alignItems: 'center', + justifyContent: 'center', + flex: 1, + }, + textInput: { + flex: 1, + height: (Platform.OS === 'ios') ? 26 : 36, + borderWidth: (Platform.OS === 'ios') ? 1 : 0, + borderColor: 'white', + alignSelf: 'center', + paddingLeft: 5, + marginRight: 10, + marginLeft: 5, + borderRadius: 3, + opacity: 0.7, + color: 'white' + }, + title: { + fontSize: 18, + color: 'white', + fontWeight: '500' + }, +}) diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index 4a18009..8e1686d 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -131,7 +131,7 @@ export default class Trending extends Component { onSelect={(tab, index)=>this.onSelectTimeSpan(tab, index)} /> } - _tabNav () { + _tabNav = () => { // 优化效率:根据需要选择是否重新创建TabNavigator // 通常tab发生变化,才需要重新创建 // TabNavigator @@ -229,6 +229,8 @@ class TrendingTab extends Component { // 移除监听器 EventBus.getInstance().removeListener(this.favoriteChangeListener); EventBus.getInstance().removeListener(this.botomTabSelectListener); + // debugger + // this.loadData = null; } loadData (loadMore, refreshFavorite) { // 加载数据 diff --git a/js/page/about/AboutPage.js b/js/page/about/AboutPage.js index fa796ba..96c7b91 100644 --- a/js/page/about/AboutPage.js +++ b/js/page/about/AboutPage.js @@ -24,7 +24,8 @@ export default class AboutPage extends Component { } } onClick (menu) { - let RouteName, params = {}; + const {theme} = this.params; + let RouteName, params = {theme}; switch (menu) { case MORE_MENU.Tutorial: RouteName = 'WebViewPage'; diff --git a/js/redux/action/ActionUtil.js b/js/redux/action/ActionUtil.js index e8a953f..7c85f88 100644 --- a/js/redux/action/ActionUtil.js +++ b/js/redux/action/ActionUtil.js @@ -46,4 +46,10 @@ export async function _projectModels (showItems, favoriteDao, callback) { if (typeof callback === 'function') { callback(projectModels); } +} + +export const doCallBack = (callBack, object) => { + if (typeof callBack === 'function') { + callBack(object) + } } \ No newline at end of file diff --git a/js/redux/action/action_types.js b/js/redux/action/action_types.js index 777236d..1e92628 100644 --- a/js/redux/action/action_types.js +++ b/js/redux/action/action_types.js @@ -2,20 +2,31 @@ export default { THEME_CHANGE: 'THEME_CHANGE', THEME_INIT: 'THEME_INIT', SHOW_THEME_VIEW: 'SHOW_THEME_VIEW', + POPULAR_REFRESH: 'POPULAR_REFRESH', //popular数据刷新 POPULAR_REFRESH_FAIL: 'POPULAR_REFRESH_FAIL', //popular数据加载失败 POPULAR_REFRESH_SUCCESS: 'POPULAR_REFRESH_SUCCESS', //popular数据加载成功 POPULAR_LOAD_MORE_SUCCESS: 'POPULAR_LOAD_MORE_SUCCESS', //加载更多 POPULAR_LOAD_MORE_FAIL: 'POPULAR_LOAD_MORE_FAIL', // FLUSH_POPULAR_FAVORITE: 'FLUSH_POPULAR_FAVORITE', // 刷新popular页面的收藏状态 + TRENDING_REFRESH: 'TRENDING_REFRESH', //TRENDING数据刷新 TRENDING_REFRESH_FAIL: 'TRENDING_REFRESH_FAIL', //TRENDING数据加载失败 TRENDING_REFRESH_SUCCESS: 'TRENDING_REFRESH_SUCCESS', //TRENDING数据加载成功 TRENDING_LOAD_MORE_SUCCESS: 'TRENDING_LOAD_MORE_SUCCESS', //TRENDING加载更多 TRENDING_LOAD_MORE_FAIL: 'TRENDING_LOAD_MORE_FAIL', //TRENDING加载更多失败 FLUSH_TRENDING_FAVORITE: 'FLUSH_TRENDING_FAVORITE', // 刷新trending页面的收藏状态 + FAVORITE_LOAD_DATA: 'FAVORITE_LOAD_DATA', // 加载favorite页面数据 FAVORITE_LOAD_SUCCESS: 'FAVORITE_LOAD_SUCCESS', // favorite数据加载成功 FAVORITE_LOAD_FAIL: 'FAVORITE_LOAD_FAIL', - LANGUAGE_LOAD_SUCCESS: 'LANGUAGE_LOAD_SUCCESS' + + LANGUAGE_LOAD_SUCCESS: 'LANGUAGE_LOAD_SUCCESS', + + SEARCH_REFRESH: 'SEARCH_REFRESH',//搜索数据加载 + SEARCH_CANCEL: 'SEARCH_CANCEL',//搜索取消 + SEARCH_REFRESH_SUCCESS: 'SEARCH_REFRESH_SUCCESS',//搜索数据加载成功 + SEARCH_REFRESH_FAIL: 'SEARCH_REFRESH_FAIL',//搜索失败 + SEARCH_LOAD_MORE_SUCCESS: 'SEARCH_LOAD_MORE_SUCCESS',//趋势更多数据加载成功 + SEARCH_LOAD_MORE_FAIL: 'SEARCH_LOAD_MORE_FAIL',//趋势更多数据加载失败 } \ No newline at end of file diff --git a/js/redux/action/index.js b/js/redux/action/index.js index 3d74a0a..54a0f49 100644 --- a/js/redux/action/index.js +++ b/js/redux/action/index.js @@ -3,6 +3,7 @@ import {onLoadPopularData, onLoadMorePopular, onFlushPopularFavorite} from './po import {onLoadTrendingData, onLoadMoreTrending, onFlushTrendingFavorite} from './trending'; import {onLoadFavoriteData} from './favorite'; import {onLoadLanguage} from './language'; +import {onSearch, onSearchCancel, onLoadMoreSearch} from './search'; export default { onThemeChange, @@ -15,5 +16,8 @@ export default { onFlushTrendingFavorite, onLoadLanguage, onThemeInit, - onShowCustomThemeView + onShowCustomThemeView, + onSearch, + onSearchCancel, + onLoadMoreSearch } \ No newline at end of file diff --git a/js/redux/action/search/index.js b/js/redux/action/search/index.js new file mode 100644 index 0000000..843702b --- /dev/null +++ b/js/redux/action/search/index.js @@ -0,0 +1,135 @@ +import Types from '../action_types'; +import { _projectModels, doCallBack, handleData } from '../ActionUtil'; +import ArrayUtil from '../../../util/ArrayUtil' + +const API_URL = 'https://api.github.com/search/repositories?q=' +const QUERY_STR = '&sort=stars' +const CANCEL_TOKENS = [] + +/** + * 发起搜索 + * @param inputKey + * @param pageSize + * @param token + * @param favoriteDao + * @param popularKeys + * @param callBack + * @returns {Function} + */ +export function onSearch (inputKey, pageSize, token, favoriteDao, popularKeys, callBack) { + return dispatch => { + dispatch({ type: Types.SEARCH_REFRESH }) + fetch(getFetchUrl(inputKey)).then(response => { + //如果任务取消,则不做任何处理 + return hasCancel(token) ? null : response.json() + }).then(res => { + if (hasCancel(token, true)) { + //如果任务取消,则不做任何处理 + console.log('user cancel') + return + } + if (!res || !res.items || res.items.length === 0) { + dispatch({ type: Types.SEARCH_REFRESH_FAIL, message: `没找到关于${inputKey}的项目` }) + doCallBack(callBack, `没找到关于${inputKey}的项目`) + return + } + let items = res.items + handleData(Types.SEARCH_REFRESH_SUCCESS, dispatch, '', { data: items }, pageSize, favoriteDao, { + showBottomButton: checkKeyIsExist(popularKeys, inputKey), + inputKey + }) + }).catch(err => { + console.log(err) + dispatch({ type: Types.SEARCH_REFRESH_FAIL, error: err }) + }) + } + +} + +/** + * 取消一个异步任务 + * @param token + * @returns {Function} + */ +export function onSearchCancel (token) { + return dispatch => { + CANCEL_TOKENS.push(token) + dispatch({ type: Types.SEARCH_CANCEL }) + } +} + +export function onLoadMoreSearch (pageIndex, pageSize, dataArray = [], favoriteDao, callBack) { + return dispatch => { + setTimeout(() => { + if ((pageIndex - 1) * pageSize >= dataArray.length) { + if (typeof callBack === 'function') { + callBack('no more') + } + dispatch({ + type: Types.SEARCH_LOAD_MORE_FAIL, + error: 'no more', + pageIndex: --pageIndex + }) + } else { + //本次和载入的最大数量 + let max = pageSize * pageIndex > dataArray.length ? dataArray.length : pageSize * pageIndex + _projectModels(dataArray.slice(0, max), favoriteDao, data => { + dispatch({ + type: Types.SEARCH_REFRESH_SUCCESS, + pageIndex, + projectModels: data + }) + }) + } + }, 500) + } +} + +/** + * 拼接url + * @param key + * @returns {string} + */ +function getFetchUrl (key) { + return `${API_URL}${key}${QUERY_STR}` +} + +/** + * 检查指定token是否已取消 + * @param token + * @param isRemove + * @returns {boolean} + */ +function hasCancel (token, isRemove) { + if (CANCEL_TOKENS.includes(token)) { + isRemove && ArrayUtil.remove(CANCEL_TOKENS, token) + return true + } + return false +} + +/** + * 检查key是否存在于keys中 + * @param keys + * @param key +*/ +function checkKeyIsExist (keys, key) { + for (let i = 0, l = keys.length; i < l; i++) { + if (key.toLowerCase() === keys[i].name.toLowerCase()) return true + } + return false +} + +/** + * 检查该Item是否被收藏 + * **/ +function checkFavorite (item, keys = []) { + if (!keys) return false + for (let i = 0, len = keys.length; i < len; i++) { + let id = item.id ? item.id : item.fullName + if (id.toString() === keys[i]) { + return true + } + } + return false +} \ No newline at end of file diff --git a/js/redux/reducer/index.js b/js/redux/reducer/index.js index 644aa2f..8f712d0 100644 --- a/js/redux/reducer/index.js +++ b/js/redux/reducer/index.js @@ -4,6 +4,7 @@ import popular from './pouplar'; import trending from './trending'; import favorite from './favorite'; import language from './language'; +import search from './search'; import { rootCom, RootNavigator } from '../../navigator/AppNavigator'; // 1.指定默认的state @@ -23,6 +24,7 @@ const index = combineReducers({ popular: popular, trending: trending, favorite: favorite, - language: language + language: language, + search: search }); export default index; \ No newline at end of file diff --git a/js/redux/reducer/search/index.js b/js/redux/reducer/search/index.js new file mode 100644 index 0000000..3a9c45d --- /dev/null +++ b/js/redux/reducer/search/index.js @@ -0,0 +1,62 @@ +import Types from '../../action/action_types'; + +const defaultState = { + showText: '搜索', + items: [], + isLoading: false, + projectModels: [],//要显示的数据 + hideLoadingMore: true,//默认隐藏加载更多 + showBottomButton: false +} + +export default function onAction (state = defaultState, action) { + switch (action.type) { + case Types.SEARCH_REFRESH://搜索数据 + return { + ...state, + isLoading: true, + hideLoadingMore: true, + showBottomButton: false, + showText: '取消' + } + case Types.SEARCH_REFRESH_SUCCESS://获取数据成功 + return { + ...state,//原始数据 + isLoading: false, + hideLoadingMore: false, + showBottomButton: action.showBottomButton, + items: action.items, + projectModels: action.projectModels, + pageIndex: action.pageIndex, + showText: '搜索', + inputKey: action.inputKey + } + case Types.SEARCH_REFRESH_FAIL://下拉刷新失败 + return { + ...state, + isLoading: false, + showText: '搜索' + } + case Types.SEARCH_CANCEL://搜索取消 + return { + ...state, + isLoading: false, + showText: '搜索' + } + case Types.SEARCH_LOAD_MORE_SUCCESS://上拉加载更多成功 + return { + ...state, + projectModels: action.projectModels, + hideLoadingMore: false, + pageIndex: action.pageIndex, + } + case Types.SEARCH_LOAD_MORE_FAIL://上拉加载更多失败 + return { + ...state, + hideLoadingMore: true, + pageIndex: action.pageIndex, + } + default: + return state + } +} From 63ce4b2a893d44c5d1d3940a69ad558c449acf02 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Sat, 6 Apr 2019 17:13:42 +0800 Subject: [PATCH 08/20] Search --- js/page/SearchPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/page/SearchPage.js b/js/page/SearchPage.js index 9ac40f5..838c92c 100644 --- a/js/page/SearchPage.js +++ b/js/page/SearchPage.js @@ -25,6 +25,7 @@ import EventBus from 'react-native-event-bus'; // import LanguageDao, { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; +import GlobalStyles from '../res/styles/GobalStyles' // 顶部导航tab标签配置 const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; @@ -40,7 +41,7 @@ type Props = {}; search: state.search, }), { - onSearch: onSearch, + onSearch: actions.onSearch, onSearchCancel: actions.onSearchCancel, onLoadMoreSearch: actions.onLoadMoreSearch, onLoadLanguage: actions.onLoadLanguage From 1c0634bafe76b037b35a6a68478284cf5a24a333 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 9 Apr 2019 16:58:04 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=E5=85=A8=E9=9D=A2=E5=B1=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE&=E6=89=93=E5=8C=85apk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 20 +- android/app/src/main/AndroidManifest.xml | 17 +- .../main/java/com/github_rn/MainActivity.java | 33 ++ .../java/com/github_rn/MainApplication.java | 5 + .../res/drawable-xxxhdpi/launch_screen.png | Bin 0 -> 72971 bytes .../app/src/main/res/layout/launch_screen.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3056 -> 22236 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 5024 -> 22236 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2096 -> 22236 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2858 -> 22236 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4569 -> 22236 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 7098 -> 22236 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6464 -> 22236 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10676 -> 22236 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 9250 -> 22236 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 15523 -> 22236 bytes android/app/src/main/res/values/colors.xml | 6 + android/app/src/main/res/values/strings.xml | 2 + android/app/src/main/res/values/styles.xml | 2 + android/build.gradle | 1 + android/gradle.properties | 4 + android/settings.gradle | 5 + ios/Github_RN.xcodeproj/project.pbxproj | 61 ++ ios/Github_RN/AppDelegate.m | 8 +- ios/Github_RN/Info.plist | 2 + js/common/NavigationBar.js | 5 +- js/common/SafeAreaViewPlus.js | 67 +++ js/page/DetailPage.js | 10 +- js/page/FavoritePage.js | 2 +- js/page/Homepage.js | 13 +- js/page/Mypage.js | 2 +- js/page/PopularPage.js | 28 +- js/page/SearchPage.js | 12 +- js/page/TrendingPage.js | 2 +- js/page/WebViewPage.js | 3 +- js/page/WelcomePage.js | 10 +- js/page/about/AboutCommon.js | 10 +- js/redux/action/ActionUtil.js | 5 +- js/redux/action/search/index.js | 21 +- js/redux/reducer/search/index.js | 1 + js/res/styles/GobalStyles.js | 5 +- js/util/AnalyticsUtil.js | 5 + js/util/Utils.js | 12 + package.json | 3 + yarn.lock | 526 +++++++++++++++++- 45 files changed, 839 insertions(+), 75 deletions(-) create mode 100644 android/app/src/main/res/drawable-xxxhdpi/launch_screen.png create mode 100644 android/app/src/main/res/layout/launch_screen.xml create mode 100644 android/app/src/main/res/values/colors.xml create mode 100644 js/common/SafeAreaViewPlus.js create mode 100644 js/util/AnalyticsUtil.js diff --git a/android/app/build.gradle b/android/app/build.gradle index 8f870bd..3dbb8ec 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -102,7 +102,15 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 - versionName "1.0" + versionName "1.0.0" + } + signingConfigs { + release { + storeFile file(MYAPP_RELEASE_STORE_FILE) + storePassword MYAPP_RELEASE_STORE_PASSWORD + keyAlias MYAPP_RELEASE_KEY_ALIAS + keyPassword MYAPP_RELEASE_KEY_PASSWORD + } } splits { abi { @@ -116,8 +124,12 @@ android { release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + signingConfig signingConfigs.release } } + lintOptions { + checkReleaseBuilds false + } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> @@ -134,11 +146,17 @@ android { } dependencies { + implementation project(':react-native-webview') + implementation project(':react-native-splash-screen') implementation project(':react-native-vector-icons') implementation project(':react-native-gesture-handler') implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" implementation "com.facebook.react:react-native:+" // From node_modules + implementation 'com.umeng.umsdk:common:2.0.0' + implementation 'com.umeng.umsdk:utdid:1.1.5.3' + implementation 'com.umeng.umsdk:analytics:8.0.0' + implementation 'com.umeng.umsdk:push:5.0.2' } // Run this once to be able to run the application with BUCK diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 45e4143..3108647 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,11 @@ + + + + + - + + + + diff --git a/android/app/src/main/java/com/github_rn/MainActivity.java b/android/app/src/main/java/com/github_rn/MainActivity.java index 3e492ff..862dbf7 100644 --- a/android/app/src/main/java/com/github_rn/MainActivity.java +++ b/android/app/src/main/java/com/github_rn/MainActivity.java @@ -1,12 +1,45 @@ package com.github_rn; +import android.nfc.Tag; +import android.os.Bundle; // here +import android.util.Log; + + import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; +import org.devio.rn.splashscreen.SplashScreen; // here + +import com.umeng.analytics.MobclickAgent; +import com.umeng.commonsdk.UMConfigure; +// import com.umeng.message.IUmengRegisterCallback; +// import com.umeng.message.PushAgent; + public class MainActivity extends ReactActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + SplashScreen.show(this); // 添加这一句 + super.onCreate(savedInstanceState); + // 注意:如果您已经在AndroidManifest.xml中配置过appkey和channel值,可以调用此版本初始化函数。 + UMConfigure.init(this, "5caac8a63fc1955789000d88", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, null); + // interval: 单位是毫秒,默认Session间隔时间是30秒 + MobclickAgent.setSessionContinueMillis(30000); + MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.LEGACY_MANUAL); + UMConfigure.setProcessEvent(true); + } + + public void onResume() { + super.onResume(); + MobclickAgent.onResume(this); + } + public void onPause() { + super.onPause(); + MobclickAgent.onPause(this); + } + /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. diff --git a/android/app/src/main/java/com/github_rn/MainApplication.java b/android/app/src/main/java/com/github_rn/MainApplication.java index 644775c..6380c42 100644 --- a/android/app/src/main/java/com/github_rn/MainApplication.java +++ b/android/app/src/main/java/com/github_rn/MainApplication.java @@ -3,6 +3,8 @@ import android.app.Application; import com.facebook.react.ReactApplication; +import com.reactnativecommunity.webview.RNCWebViewPackage; +import org.devio.rn.splashscreen.SplashScreenReactPackage; import com.oblador.vectoricons.VectorIconsPackage; import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; import com.facebook.react.ReactNativeHost; @@ -10,6 +12,7 @@ import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; + import java.util.Arrays; import java.util.List; @@ -25,6 +28,8 @@ public boolean getUseDeveloperSupport() { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new RNCWebViewPackage(), + new SplashScreenReactPackage(), new VectorIconsPackage(), new RNGestureHandlerPackage() ); diff --git a/android/app/src/main/res/drawable-xxxhdpi/launch_screen.png b/android/app/src/main/res/drawable-xxxhdpi/launch_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..01fcbdb670c2c025b5c324b1690bc8f0240aeaa4 GIT binary patch literal 72971 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/layout/launch_screen.xml b/android/app/src/main/res/layout/launch_screen.xml new file mode 100644 index 0000000..86d6a72 --- /dev/null +++ b/android/app/src/main/res/layout/launch_screen.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index a2f5908281d070150700378b64a84c7db1f97aa1..93248f4542880233a893efc4ba86a21e36d9facb 100644 GIT binary patch literal 22236 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

iN-ow8W!sChFbVwj)of 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

iN-ow8W!sChFbVwj)of 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

iN-ow8W!sChFbVwj)of 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 axPAxe%7rDusX+w*0000E4oWZ5i=uRa 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

iN-ow8W!sChFbVwj)of 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

iN-ow8W!sChFbVwj)of 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 459ca609d3ae0d3943ab44cdc27feef9256dc6d7..93248f4542880233a893efc4ba86a21e36d9facb 100644 GIT binary patch literal 22236 zcmeI4cTiJX*YHCCX#!UfP^5{{OCXd$Kzc8VfJhM`B%w$ZLhm3&dKW>E4oWZ5i=uRa zARvPD4l2D!f1}>wlY1}o%)IY6-#=s~Wv#W>T4(?EUhABhGbfMKZzzxw(+~py08%AI zSq;p0`S&;B8O-PPt}|fF7m=-^t~~%iLh=2L14w*&5daWbHrLWY>8Pnf;Wk!0FocaU zlE>M~7BdUBPAujO#RRZIqF{{9R+iTGQ0MDRKjK0$zrU+NOpHHhya9(5zNac2;$`j@$+(n`Jn>5PysOG?=L2CV$3fw zJA?^TLstIxbQtM6lNkzS3k89koSb-^_<3yXOhJ4iA|fDOFbE9h#*E;$cd1UR7G-W@&G@j|P|LA9HW`nY^H?#Sdlz*E4H8EyQ)YSgW z{g20MW%ZA#?NM@$7zDo~{YOuGEf-rPNCRna<6s9z$~j`Da`m5Pk1{v;YjOS~(s#?h z9y`+6{9oL@TYkFz*a|;aNDMPqsEi#FhO)8Kvazug|GCT5|3JtnBlBZdF|yq;w?^1F z*|Up*uwDLI@4uoUWnn0!IA%Wzar26B^MSSa_@R7YC@+|kR~X96`%93YBe7zr*&xhK zT>dGBFjRo=*BHNy#EyZf9|Q~q`>&$>I`)@12sqTl#?A_c5;wPknIb{9)}~^hzm&g> z{Bu`9Wo#^M>@bIg6z3NM{ge6^uOB-Ns$^}Cf?30nO0wb@1&_Ho0xBYi5P=B6Ot^XB z!Z2MN_&Buon;ub*&A-RP`ganXqIMPG}4ErO|FTwtn=%$@HrtiTle;M~p$IWjdAjl2lGdAG{Lj(kc`9y?xk-~q@hS? zc_0yDp#RYRby$CT{_bY}_vQTW5d4h)i~GOla56($pHyByL;V=_??LQsOi)fRJEW8; zW{3PYmH#v9f4X6Z7Xy7i7g(DAKUfHRnB)I$8UDMf`d==?uN~_D-!lBQi{WN4Yf~gb z9Q4Nq{zLKKt=!M`{Ljkzqapmg62-osoS>L0#_TBAkLuwQ1N}|+SLUDNb-q`lrNfW$ z5HK(11>*bu`Mc|{W^7m8lU;u`W4r$4dE43?B@X^Q8|_6zKmMB1|7BFHSf@#_fSfFz;yRfRmh}`D7Lb$0Q(PzW!Lpv> z!UA%#c#7*}K3LXMTv$L(7Ef`V%m>SQiVF+K$>J%lllfp-PjO)ZIaxf#buu3;>nScQ zASa8bxK8GSWj)1(1>|J$6xYdou&k%Juz;K_p5i*050>>57Z#9{#Zz1-^TD#7;=%%Q zvUrN?WIkBdQ(Ra;P8Lsboy-TzdWs7R$jRatsGy)>B+qKu#7<#6|qqL!3x!%#)i=m`60p%v_T(k9RV{6*bfV09R%Jz}pW1*geL4 zF9HA#ya2$G5dZ*9005|Mq79qn0RUPvC0Qvg=ix8OPF0ix11(%2biyEp<^W%y zqpz_7JU!@K$z{Pa5^AA|tqNg@A2ax#l^CmWv9$n&g3~{~_GRN}x*cqbQ?14RINa&O zPOG{7+?VV8Iir2st|>nD^Y5Mvm2KA!dJgKy89T3n()Vy2_x$P$}lT@fJ5yt9ht8pU}NuZS@6P?@~Cnz8` zM6tWb!(zeM4|)E?Z26OseKER5x5Xe zAf2?V#~B9TY|NP~8I435>LYa}9~0&}eOdH+-E-Miep_L#&Lj?rV*vDCwv{ZsEpJnh zJlG@M;L^nL4p`HMocBCuGz~1fyvmc=`L;9i`HpU~t|WDdzxDX#MD42&EVy_&xq@;Z zW&};R+tPuz?&S%SEHK5*eadiLfKAZ$_E@L3N*)eOYdcY)8#ton93KI)uO zUh?KV^N=#<&f>*XNMX|@H=lvD@A}T_ z(&cSm8UWTvmKNT)$=W2Vs7mF&^hUdg3XLDV2jxzVERKghr<2`v>grTC?VF-P`%j=P zsPM_;m1PA8FTRQmiWeRiGmW#rF#zZajXt~9Dt$3sqsi%!MNs%NM^Z?E%O#o$okKlI zNP4~jp2Q8(Y>MpF9>=Wb@uwVWwi*g)i2HR~afmnL$T|4wHg3cv5{R zYKzneN-=gWl8V%;t*XhQ4FfJj-9j%42Y?gqX01`hO*-}V2+>>NBs=kXNl;O_(_Cgv z41Il3{pCk0(r?YLMAMbxYZl`b^gE(VB^&KSE=53TU{fEYdAD>b`&%-&Q#T0bKq=le zMQ{8^Rmsv9X}za+J>PJAtm@zx147A5P0oS0dv-%aKTD1kQwkN8)l||#$v?P^#1%Wh zU)-GEzskVCmv51I$Y zh@pfJ?oJdB2^H84ehNJ@LGH;&Cy840q2evx;IcXg{?RN%E#-KgAlF2Rwa` zNi|g700%V&vghq_rgQn)lekWEFud(1{n(EywNu*jX}|{hRVW6PClyN;5;1L)#mnEJ2Lzh zZ1BRcP+TRymw5(cm@e!Pr9}Rk^D%3gu2iFJE=%Bf$C{jG- z45E1(G;*goWzxQWBK8EskDD_$o^1ICA}PCrOBSsFZI^c-m&x z{CNp4>HrnPvU{1|7$=!Em~WSFrdfqzKgpTnI!q+yN_guW2kGk|(p>vzy*+KgV{Zf< zR0jENCx)KXPeY2B<78YiSXe%fpaDrAnjA&-B_HD@C>)XPelfSw=pkWC#`SNJkd6UYXHws`!Es>1CcDG#|wr$2sl3f5@7(6O=un{qF)2(O2hIT`Sgzl4RA9QYs&G;rN$?`aI^-_k6eseRPe<(9r= zvUkHl!!xENj5FBy?z7&Nl~?9EZnQp2ZptPrwKKK(**;2NwK~&6v|Gl{i>Su)3e71B zP<`xOB_T3>?y%Xs8IoX;u(^L_2XWAGzuhV23hOQFqGaZbE7~MEn3Gl((sT2|HxpG} zfLOdP{wYE$B=IRh!=vHZL z1{-2PdM3bN@-yF2r?pGPZI${P8r1Agbq7-R$yXEI+@4+8^GDYn10B}C4%HtIXFXq8 zTC#e3hqc#2+~b4YLV9FkWz^@I*ByEcjE~4)sl{=#Yxg)6kdALHomB{_$c2HBMSi3f z?jLfc3! zfqil*(mJu7nxTrgY_5*h-2v`8`sP`Yft|!!ZWdwNY{kuT+;>Teb0p5aD3D~#F5ra%VBy- zo#8v3U9DJU{E8&emH?lYXhxq&vG?S-WOYEj%ASbu7oX;zO1G(SX6#~urTxT(?shNDzt8Av~F9ng4CNH{}7Ge0ILF$MK$v-T_J)hhn+G06kNx< z_4p{qv}(#WIt4jl`!s1=sb1n9oky?Wk3H7&%4$R=DO49*mQ1~XD`J*7$x%UmBQ65@ zg(`T5Aw>P2LsE^OKcU*5b$=*$xNEq?%?BkA#wA#L!U@}TCs`<8bMGn~)X!zrZL!8T{{A)*-sx$LbKi6d0 zujf>ur>v#p!F&l?;rIkbjln_3h4C9DvhmsM;!vN?jUMVuC~F zyj)gGd04oyK*d)=@P40|oNq%YpesSE*S#eYFPJ&uscf}og@xy?HpznI?gN(f1$^^R zT_MDFoyYyN&g?3aHJm~fuOqCD8rIv-p67k=ivMu$Gtq8IchOjie#cBC^vuYiOSkE2 zcLo)@A#T0RKM@f?t;mtOzoR4}xN z7DiKWDl|LTlsI(P^xJFs-Ehu%QRz}7zg;I^p+hczY>%Og^<^D&WL9YHAQ0LNl@Q`m zAsJHSo4Xrt(|&j6ePJ`e$9(jr}jxfC` z(Ysl-d7awx)uq(nDUOd9>_98z&!l)3iQy{)!VmmK5KS`p{*mXSUP^xU^>aDAKT|qt zG4jU<0$j=z$`w{b1St=fdKqfAe7-_u&(IVY;Dnek+2=I%^A*3~fy{Q6 zEcdStp&#r>o7FcsQzg3W-^x8^zsro53@K>PA$-8Q0zm-#vPpRt@e%Jy!unPPkYMiB zT>Ro4%7zpjHc4ut*^k9%T($~FRAsk9H`Kj3)T#4n-`Nwwje6|FH?z)CAo_Iu zZi?+hw}U9pDmTN@SjEm;Cckk^qt9at9}7hom-l^59(75+7Bas8>Mp-}>k1T<#ZFQ3 zTOGKs7RBXoP-8RZKl>8hV1Wecjd`)`#J*a~VU9*7wZBkJjGwGT8LLl615!DG@kf-T z*JEr~EV)d(W{B}OTQiJahsry&tgAhz6u)9c3iK$tS@wYa9mNQ9=vB5Mh#h|0!D23u zi`&3ivT+099wyihl&o3qN69o=RZZ_`+V$(3^EG3l04D|z_Wv~&R(-BR;S zmeJfHL#Y&^-r!0tEp>M3?__}4{zXn%g6SmBsT@1;t;m%2#ujNLU6 zPa4)+?;p&eRxTgr3v@rRro2WZGNUcMbn|Y{&AqR6Hbz^e#AWChg|rH5qLE{n!TRP( zcFmMR0h1TB{3Jv({)6?z+%KXkp9y?@`FuRE`K4Eyn+4Y_%|2fCKpKB#_c!(UZz;jy z@`f}P`9Q7)Vm8f#oxJ$;r8&t@$sBFcH(mLjH|H^0%$9JiS;s!S9d5&!zgvHCISJA0 z<;F_8X))8)8sj-%AGq_>x%6QNT}_COlxe%_Ecze}_S}5sx-3N#>a%*3yVu%++WTCJ z*+IV#T?LFJ^~UGkCD2yhz1R7;ZpCovF`vrx(LE*M8@Xknz}yXMzbk{XH{ADO(YGnp z6iIf<9$A~Gh2$!(-q7=$*Wjv|lV9c@HWsy160m$?$FR1z=HBGd!5TdEl?G2ekK>Wu zyCYg!f@B6$<$M=-*K&dM0f)4EP3yNUzasCxqKJbMXEH8*GfP4xcY!_sD&wwfy7JgG zt`1h~(B+}RfEZMLp!Fr8M6~!DSw3IKy42yeQbel0_o#D^n$v zN=8gc2U4XaHT^W4A*X&=stxp*rv zGI3vJ7QS}C#f#J5)bmJ_<|ys0<;5lez1b~+PI`36Jd3C!jzJNTEJu*BGY97*KY900mv2)MlC4+Rz??tI`;G);xY-aKp z;OmwG2wF)KVio-|?IeaBWDm`&s=Y6NFo(SB*Ar7Bi%YZbuj^s`cs`Gptguseo1-J) zxAj#+2@+bzo{ejtmusE~e5wEnqYgRxeOG|{<^@^2!Vw)5A%aj*zDthOT9twl`e>kD ztGSvOBw00;ID?>v6n-Nq$h1+a36jVg)^VX!z_o69rP5p%hst%ANk=28b}i{V?XF@p zZn(eC{2p$;3+J@0=IryyssRcw<;dq*8Uc|{qi1YQE@niMA&Iyu16ndSCAnX40?;ZJ zQP1LO3arKCzTm5Vp=lk55z%_t!d6RfYmD+lwFs{15(QnnD9P?wAI2jnn}rLf89YIgM27~lh3o2MSiZDa>G)9#RdO5PI@q!qz)-6lC>v~QrnLYR5E z6dX4odkZZbwy5Jox$Yj#|8<};flk+;8o2eo4L61ucVU2YYMMl=tq@EnKlORy>CT`n zWEgpqJC2W;MT^i>x#{7XYcE-Ew!%n)QFutOU&j|}pWZQx`DIGL!rnl`5Vr(AzMhnt zs;KqW-r;%2&vfU}wHEv_K2(DQc%Ha!zOeuDpMX!7$f;#2_ByH}R zs>1Vr8*@~bl`?$-xES4Ht!dyYxv`|Eiz{sXu_He6R-n3{kK$Pp^{|9m+nmWLfXb+Y zjae-`<4xXLtBlRidFy}x)9k?aT>v7{tH_AM)N!K8PMuMzy|Y1~q>e?Puh%+!Ga&

iN-ow8W!sChFbVwj)of zabsgXoq77flYiGW<$+TXUFy6KC2&F8%C2%PV%(FXMBWbl>RqR6Z`e8UWHQC4Vt$Yy z4q=h^Ryqb23ltvYfpgrPc<4_rd&Z`|J&;=2xnB0M;N{nF+t*u1q%B0R5ABH%Vm;kX zfjObUT?vSUE#IZkiYP(5?8o#{##X!n? zxt|j1U_(2PsHAO{uC&7GagT3fG@)U#_(Ce97BBv+hu)dSKNe`aK$rUI-fkZ6POnHH zoJgHA7jein~$iG$k;ClW&9D9>j`*2tI4(qv9zN!JXwl=xzB0c; zhx%NSrAck&jC|phMDCk!FdQ@w5GeoRl(8w}Y9< z*4VWiH>r_R>&a~S>wAcZq%eI=?I0X>hdNG~m}G%|zv(Ctf1^<~eCspQWVv0uA9*0fjZTkj z!7nSIhBJ=ipMX%M?wV^7+9tDhIxo`?3N|O~OE;w2(B7zpvv)?cP)lrLsRYURZd>g= z`C&+hS@H!qfz;q=-{pj7B+5>QqfxQ-_~r85)dCx~$>VqHhpV+7(u%fubsHu0dM(m6 z(i5{L--~_0Mke`8`?-DJI0kklWn^95xc(3hHWxyDsy~qOP=x5{B-Qo?-@9bX zRk!L`Vv*MHA~(3toci1Tn`#GR@!eE#KFTs`+g)eA+hFq19c5|K+j4i(`#|2B-5mx< z)x%6MAi1dDsn2R+mk9l07zl+|+sNKO!(sN4Su?9aF#CW%0FWfa{F(8Nx0gB7mk$|; ze)Xr`*6;C-_-PUacgRKji6poU&lELkjTwy<(`y&K4-_m*v$)zS)riBK75pNddx+jm z;X-0aoT8yuNgYnTyEz^@8}{;v&LZ{LL%7#%yBE0}OYi9%1;n36fy z??@pwpW}scdaO*3-{e(|0(EHW(TZlVnUo9Ab;)$GtFj-*tH{HsC8F3ZB3fJSc6%&p zp?dYp=O=!9^Jl&dew`&+*ct1#mK#i)Z<<*|m)>sAORoa^!w@YH+xO>+O&oyle)D|! z%qjboCqW!}!ng;?jV|96aq;s3PjB-K=N55PGwm#q8Xf`m>1f|kJ9v#dtGVdTKPWPp zW7`>H$R3QRNKgs2tIGPi&AY~5buc^u%f1(K*z2~6zCL@)jb!dkkXKH%$s=E{5po81 zoLW@cNaiM6;2m*y>TU*Ni2~IXrO^0J=Azuj+-e}F(H?EWCjO>qta9voCt^QYyc7>&>jwJxZ(J6( zU8Jjn`XM{l=em7-aE+c@40@Y%8Ld9IJ(9Eicn)38PRX-1OKw(K$45n9M?L1HaD3?h zX2M4yBm#6ixwATDAJtgeS3K$vsERv||%$!L4$ z9H4TjTY3Np#oe8S$Rs9>-Gmipmu2hw~@!bBkE5Yv+XdT%%hg+B4 z0*8a+TJV3~s5?($_Wj=6f84CYsD9k6V~5^iV$vqn?vP6ahLk*!=A6BU(QdAI7f)tS=AhH53iU?Q%B}x&gA$2B`o|*LCD1jhW zSQpS0{*?u3iXtkY?&2<)$@#zc%$?qDlF1T~d7k&lWaiv^&wbx>zVm(GIrof<%iY)A zm%|rhEg~Z$Te<*wd9Cb1SB{RkOI$-=MBtc%k*xtvYC~Uito}R@3fRUqJvco z|Bt2r9pSOcJocAEd)UN^Tz-82GUZlqsU;wb|2Q_1!4Rms&HO1Xyquft~#6lJoR z`$|}VSy@{k6U652FJ~bnD9(X%>CS6Wp6U>sn;f}te}%WL`rg)qE4Q=4OOhk^@ykw( ziKr^LHnAd4M?#&SQhw8zaC05q#Mc66K^mxY!dZ=W+#Bq1B}cQ6Y8FWd(n>#%{8Di_8$CHibtvP z-x#-g;~Q?y0vJA*8TW>ZxF?fAy1DuFy7%O1ylLF(t=ah7LjZ$=p!;8(ZLjXAhwEkCR{wF`L=hwm>|vLK2=gR&KM1ZEG9R~53yNCZdabQoQ%VsolX zS#WlesPcpJ)7XLo6>Ly$im38oxyiizP&&>***e@KqUk3q3y+LQN^-v?ZmO>9O{Oq@ z{{He$*Z=Kf_FPR>El3iB*FULYFMnLa#Fl^l&|bFg$Omlh{xVVJ7uHm=4WE6)NflH6 z=>z4w{GV&8#MNnEY3*B7pXU!$9v-tZvdjO}9O=9r{3Wxq2QB}(n%%YI$)pS~NEd}U z)n#nv-V)K}kz9M0$hogDLsa<(OS0Hf5^WUKO-%WbR1W1ID$NpAegxHH;em?U$Eyn1 zU{&J2@WqSUn0tav=jR&&taR9XbV+Izb*PwFn|?cv0mksBdOWeGxNb~oR;`~>#w3bp zrOrEQ+BiW_*f&GARyW|nE}~oh0R>>AOH^>NHNKe%%sXLgWRu1Sy3yW0Q#L{8Y6=3d zKd=By=Nb8?#W6|LrpZm>8Ro)`@cLmU;D`d64nKT~6Z!aLOS{m`@oYwD`9yily@}%yr0A>P!6O4G|ImNbBzI`LJ0@=TfLt^f`M07vw_PvXvN{nx%4 zD8vS>8*2N}`lD>M{`v?2!nYnf%+`GRK3`_i+yq#1a1Yx~_1o~-$2@{=r~q11r0oR* zqBhFFVZFx!U0!2CcItqLs)C;|hZ|9zt3k^(2g32!KB-|(RhKbq-vh|uT>jT@tX8dN zH`TT5iytrZT#&8u=9qt=oV`NjC)2gWl%KJ;n63WwAe%-)iz&bK{k`lTSAP`hr)H$Q`Yq8-A4PBBuP*-G#hSKrnmduy6}G zrc+mcVrrxM0WZ__Y#*1$mVa2y=2I`TQ%3Vhk&=y!-?<4~iq8`XxeRG!q?@l&cG8;X zQ(qH=@6{T$$qk~l?Z0@I4HGeTG?fWL67KN#-&&CWpW0fUm}{sBGUm)Xe#=*#W{h_i zohQ=S{=n3jDc1b{h6oTy=gI!(N%ni~O$!nBUig}9u1b^uI8SJ9GS7L#s!j;Xy*CO>N(o6z){ND5WTew%1lr? znp&*SAdJb5{L}y7q#NHbY;N_1vn!a^3TGRzCKjw?i_%$0d2%AR73CwHf z`h4QFmE-7G=psYnw)B!_Cw^{=!UNZeR{(s47|V$`3;-*gneX=;O+eN@+Efd_Zt=@H3T@v&o^%H z7QgDF8g>X~$4t9pv35G{a_8Io>#>uGRHV{2PSk#Ea~^V8!n@9C)ZH#87~ z#{~PUaRR~4K*m4*PI16)rvzdaP|7sE8SyMQYI6!t(%JNebR%?lc$={$s?VBI0Qk!A zvrE4|#asTZA|5tB{>!7BcxOezR?QIo4U_LU?&9Im-liGSc|TrJ>;1=;W?gG)0pQaw z|6o7&I&PH!*Z=c7pNPkp)1(4W`9Z01*QKv44FkvF^2Kdz3gDNpV=A6R;Q}~V-_sZY zB9DB)F8%iFEjK?Gf4$Cwu_hA$98&pkrJM!7{l+}osR_aU2PEx!1CRCKsS`0v$LlKq z{Pg#ZeoBMv@6BcmK$-*|S9nv50or*2&EV`L7PfW$2J7R1!9Q(1SSe42eSWZ5sYU?g z2v{_QB^^jfh$)L?+|M`u-E7D=Hb?7@9O89!bRUSI7uD?Mxh63j5!4e(v)Kc&TUEqy z8;f`#(hwrIeW);FA0CK%YHz6;(WfJz^<&W#y0N3O2&Qh_yxHu?*8z1y9Ua}rECL!5 z7L1AEXx83h^}+)cY*Ko{`^0g3GtTuMP>b$kq;Aqo+2d&+48mc#DP;Sv z*UL^nR*K7J968xR0_eTaZ`N`u_c#9bFUjTj-}0+_57(gtEJT|7PA12W=2Z>#_a z&Wg@_b=$d~wonN3h~?)gS`qxx<4J&`dI*rH9!mTSiQj(0rF-{YoNJRnOqd5IbP7p} ztDaPu$A;#osxf=z2zVe4>tpa(knS_Mp67nKcE<>Cj$G2orP(Z$Oc4;4DPwbXYZsS^ z;b>59s(LgYmx|tkRD?U{+9VZ$T}{S}L6>lQNR^a|&5joAFXtOrI07Do!vk(e$mu@Y zNdN!djB`Hq1*T8mrC@S)MLwZ`&8aM8YYtVj7i)IY{g&D1sJaY`3e=1DSFnjO+jEHH zj+|@r$$4RtpuJ!8=C`n5X;5BjU2slP9VV&m0gr+{O(I}9pYF32AMU?n$k$=x;X^E# zOb-x}p1_`@IOXAj3>HFxnmvBV9M^^9CfD7UlfuH*y^aOD?X6D82p_r*c>DF)m=9>o zgv_SDeSF6WkoVOI<_mX};FlW9rk3WgQP|vr-eVo8!wH!TiX)aiw+I|dBWJX=H6zxx z_tSI2$ChOM+?XlJwEz3!juYU6Z_b+vP-Y|m1!|ahw>Kpjrii-M_wmO@f@7;aK(I;p zqWgn+X^onc-*f)V9Vfu?AHLHHK!p2|M`R&@4H0x4hD5#l1##Plb8KsgqGZ{`d+1Ns zQ7N(V#t49wYIm9drzw`;WSa|+W+VW8Zbbx*Z+aXHSoa!c!@3F_yVww58NPH2->~Ls z2++`lSrKF(rBZLZ5_ts6_LbZG-W-3fDq^qI>|rzbc@21?)H>!?7O*!D?dKlL z6J@yulp7;Yk6Bdytq*J1JaR1!pXZz4aXQ{qfLu0;TyPWebr3|*EzCk5%ImpjUI4cP z7A$bJvo4(n2km-2JTfRKBjI9$mnJG@)LjjE9dnG&O=S;fC)@nq9K&eUHAL%yAPX7OFuD$pb_H9nhd{iE0OiI4#F-);A|&YT z|A3tvFLfR`5NYUkE?Rfr&PyUeFX-VHzcss2i*w06vn4{k1R%1_1+Ygx2oFt*HwfT> zd=PFdfFtrP1+YRs0AVr{YVp4Bnw2HQX-|P$M^9&P7pY6XSC-8;O2Ia4c{=t{NRD=z z0DeYUO3n;p%k zNEmBntbNac&5o#&fkY1QSYA4tKqBb=w~c6yktzjyk_Po)A|?nn8>HdA31amaOf7jX z2qillM8t8V#qv5>19Cg_X`mlU*O5|C#X-kfAXAHAD*q%6+z%IK(*H6olm-N4%Ic)5 zL`?wQgXfD&qQRxWskoO^Ylb>`jelq;*~ZIwKw|#BQjOSLkgc2uy7|oFEVhC?pcnU+ z^7qz}Z2%F!WOp%JO3y*&_7t;uRfU>)drR1q)c7lX?;A1-TuLTR zyr(`7O19`eW{ev;L%`;BvOzh?m|)Rh?W8&I$KVvUTo?@f@K!du&vf=o6kKb?hA z%e6$T0jWS7doVkN%^_k3QOksfV?aC$Ge$a)z(!C@UVs*@qzDw*OFd*JfX#>5LCXjE z_vfUrLF7D`K$U2Ld#OCnh9U!;r7%GlKo$e__Il-oba06ER{H&f#J&W@x^^5j;y$0` zs2`m6pf+{UiDb{Mjsb$rH+MCM6G_wX92so96`ODFYKD>!Xz^0y@U7Tc1uON4L<>2f-oPe%FRPEZ@S#-yd7Md-i?v z)$Kgtq;%4g@>Kap3Nl2I&jnCIfGmRmcF4CXfF1H}3SfhLg8=!a0ucGaUk&c3*Ykgl z2X_L84cs+FD#cjf-nMJkVDH%XzOoh5!X-Q$K5VZx-hGF7MQ=XKBjhZZQ@1Sh zO^vY`WQ`zi21z-+01na%<^niMFIWm-n|!?hm4X2HEHkba4YS|+HRoIR=`#Xck@PFXaPjnP z=hC4A*0lumS+gpK=TUN!G;{WqICbMz-V=-lTP^@a#C|E!qH;T00SZh7u#?+?08g0< zV1s%-U-`T@8wGh!3pO^`zUIY{nAED7kBqg!qi&GfOp>57f2PGTV19m z0qU@1PYkf%4z_%;Sq4IY94rS+ie~pwT@O3+tg?#k_=5PIk6tV@< zwLoqM0wBVLkI#`|1w=eYMnc^aRR!t?lnUng>WekR#X!!9mYXL3g^gC7`)S7mmo{y} z9*N!d$s32Nu{cZp#O|UxEZK7eY<7hGcI=lc;HrSVL|HA|S$rhhu_DBT&l+`75d`Sj3LaM~H)P zZuk2&jor6yipafklSsPL-vMo?0yAYXpH3=LveBhkno-3{4VLWL16I-@!RM$Po>&}} zm&PX3-$i>$*yx-THZmvK2q`8Qm7B`(NMR;>VSgoGw}W|G6Xd6v04Zf;HIZ0DZU?@- z39vPe0N8w(9kl$2?eG4T?tLgY5V&aFl%~g;2)aSpi!dl?{hDgsz|3<-M(gPtwP_!n z2aB4tV?d0k+>X`+(HMYfK@qtfDK|mIJeg+A<_i-n+5wkrexFs#V0N&~+{+qJ(wggC*52o2daaRwcu7r;S!!KwguB3!Ei7?IEY ze4V$m{8B4Q^(VK4~Ea!V@@}Gs0HGbR5 zy~WI*21hZuoiK`=O$2a|Uce-Zi2%A*pB|?{gv)n8+_B+i&u8Ys)ePY+UwhBDlzbC& z+N00*-?a8DTC26*(3pKgeMO`fOau^-+c6Qqq}3-dpTsEEH}ds! zT^}8XAWO>c5%+qF%#M8#x_0gC+N%q8h6-%w;qidS%gai<T)vpfYuCHXRx6O-TbC|fnj87X zBESvn(9XlXFMj6%{&BaNQ&;xixaKP)+jJ|%u&?HXvYficY}{%hf?0rNDS-X-0_Jcr zjfj~n?T;~RL#sd4ZED2Jf{*Vj+*1eP9-H+~8X^#Jb?HHabLY)EH{QD@Yh-$M`XXt@3_f-L8nBo~*C?L4~n6M92PCuzX=KFgM*j!B66er$F! z+*M(Wkk`UI@uhrL#IUz-C{K@@xtd&n-PQz%kc}7YeE{{&$?}-*yW$eG*E4jp>B_U!2`2oZuvvitN& z%RN>tE$+Yhtqb1q+xQHbp=W4uKSiIj_LZppR0=hEiVj>P0^Vcr^hu2+#Hqum+}zzo znqZ|M4oD|qd=y&JX-qob`=uqt?o%FJPIVY2w0M7BH>#sx>s#OM#9JF1(3LxMAe-vi ztJeU*G)aksP`5sP9_%|~>Pp{NmMMcay>&D+cI%H}$uSx{Su(yz$)2e$*pS%*+!Zo>DNp(P7 zI%w^D2ceEFUGCtQPKfsKr`x%^dy;Rh>lMKuhA^btz=071W=vV`_xz&m;cvd0`|!3+ z2M6uga6CNvy)%Pjw_X}5+xf###jc+?=>6chZI{BMH=haH^7ipT>(?9{weF3apk<4; z_nZFsi`@oFBXCZE^k9B1x+cH2)~9d(MnfEm;GJxG*IB zU@ly{cOTWk*K1ryX+T7m!6A>VwB-*qfH;b>`AUP19lLSA9HbfppW!={L0K)??SymOCA^V>=tOBLn2c5e ksm9QK-qMKdW>5J419kFO%DdQj-T(jq07*qoM6N<$f+5oB`~Uy| diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 8ca12fe024be86e868d14e91120a6902f8e88ac6..93248f4542880233a893efc4ba86a21e36d9facb 100644 GIT binary patch literal 22236 zcmeI4cTiJX*YHCCX#!UfP^5{{OCXd$Kzc8VfJhM`B%w$ZLhm3&dKW>E4oWZ5i=uRa zARvPD4l2D!f1}>wlY1}o%)IY6-#=s~Wv#W>T4(?EUhABhGbfMKZzzxw(+~py08%AI zSq;p0`S&;B8O-PPt}|fF7m=-^t~~%iLh=2L14w*&5daWbHrLWY>8Pnf;Wk!0FocaU zlE>M~7BdUBPAujO#RRZIqF{{9R+iTGQ0MDRKjK0$zrU+NOpHHhya9(5zNac2;$`j@$+(n`Jn>5PysOG?=L2CV$3fw zJA?^TLstIxbQtM6lNkzS3k89koSb-^_<3yXOhJ4iA|fDOFbE9h#*E;$cd1UR7G-W@&G@j|P|LA9HW`nY^H?#Sdlz*E4H8EyQ)YSgW z{g20MW%ZA#?NM@$7zDo~{YOuGEf-rPNCRna<6s9z$~j`Da`m5Pk1{v;YjOS~(s#?h z9y`+6{9oL@TYkFz*a|;aNDMPqsEi#FhO)8Kvazug|GCT5|3JtnBlBZdF|yq;w?^1F z*|Up*uwDLI@4uoUWnn0!IA%Wzar26B^MSSa_@R7YC@+|kR~X96`%93YBe7zr*&xhK zT>dGBFjRo=*BHNy#EyZf9|Q~q`>&$>I`)@12sqTl#?A_c5;wPknIb{9)}~^hzm&g> z{Bu`9Wo#^M>@bIg6z3NM{ge6^uOB-Ns$^}Cf?30nO0wb@1&_Ho0xBYi5P=B6Ot^XB z!Z2MN_&Buon;ub*&A-RP`ganXqIMPG}4ErO|FTwtn=%$@HrtiTle;M~p$IWjdAjl2lGdAG{Lj(kc`9y?xk-~q@hS? zc_0yDp#RYRby$CT{_bY}_vQTW5d4h)i~GOla56($pHyByL;V=_??LQsOi)fRJEW8; zW{3PYmH#v9f4X6Z7Xy7i7g(DAKUfHRnB)I$8UDMf`d==?uN~_D-!lBQi{WN4Yf~gb z9Q4Nq{zLKKt=!M`{Ljkzqapmg62-osoS>L0#_TBAkLuwQ1N}|+SLUDNb-q`lrNfW$ z5HK(11>*bu`Mc|{W^7m8lU;u`W4r$4dE43?B@X^Q8|_6zKmMB1|7BFHSf@#_fSfFz;yRfRmh}`D7Lb$0Q(PzW!Lpv> z!UA%#c#7*}K3LXMTv$L(7Ef`V%m>SQiVF+K$>J%lllfp-PjO)ZIaxf#buu3;>nScQ zASa8bxK8GSWj)1(1>|J$6xYdou&k%Juz;K_p5i*050>>57Z#9{#Zz1-^TD#7;=%%Q zvUrN?WIkBdQ(Ra;P8Lsboy-TzdWs7R$jRatsGy)>B+qKu#7<#6|qqL!3x!%#)i=m`60p%v_T(k9RV{6*bfV09R%Jz}pW1*geL4 zF9HA#ya2$G5dZ*9005|Mq79qn0RUPvC0Qvg=ix8OPF0ix11(%2biyEp<^W%y zqpz_7JU!@K$z{Pa5^AA|tqNg@A2ax#l^CmWv9$n&g3~{~_GRN}x*cqbQ?14RINa&O zPOG{7+?VV8Iir2st|>nD^Y5Mvm2KA!dJgKy89T3n()Vy2_x$P$}lT@fJ5yt9ht8pU}NuZS@6P?@~Cnz8` zM6tWb!(zeM4|)E?Z26OseKER5x5Xe zAf2?V#~B9TY|NP~8I435>LYa}9~0&}eOdH+-E-Miep_L#&Lj?rV*vDCwv{ZsEpJnh zJlG@M;L^nL4p`HMocBCuGz~1fyvmc=`L;9i`HpU~t|WDdzxDX#MD42&EVy_&xq@;Z zW&};R+tPuz?&S%SEHK5*eadiLfKAZ$_E@L3N*)eOYdcY)8#ton93KI)uO zUh?KV^N=#<&f>*XNMX|@H=lvD@A}T_ z(&cSm8UWTvmKNT)$=W2Vs7mF&^hUdg3XLDV2jxzVERKghr<2`v>grTC?VF-P`%j=P zsPM_;m1PA8FTRQmiWeRiGmW#rF#zZajXt~9Dt$3sqsi%!MNs%NM^Z?E%O#o$okKlI zNP4~jp2Q8(Y>MpF9>=Wb@uwVWwi*g)i2HR~afmnL$T|4wHg3cv5{R zYKzneN-=gWl8V%;t*XhQ4FfJj-9j%42Y?gqX01`hO*-}V2+>>NBs=kXNl;O_(_Cgv z41Il3{pCk0(r?YLMAMbxYZl`b^gE(VB^&KSE=53TU{fEYdAD>b`&%-&Q#T0bKq=le zMQ{8^Rmsv9X}za+J>PJAtm@zx147A5P0oS0dv-%aKTD1kQwkN8)l||#$v?P^#1%Wh zU)-GEzskVCmv51I$Y zh@pfJ?oJdB2^H84ehNJ@LGH;&Cy840q2evx;IcXg{?RN%E#-KgAlF2Rwa` zNi|g700%V&vghq_rgQn)lekWEFud(1{n(EywNu*jX}|{hRVW6PClyN;5;1L)#mnEJ2Lzh zZ1BRcP+TRymw5(cm@e!Pr9}Rk^D%3gu2iFJE=%Bf$C{jG- z45E1(G;*goWzxQWBK8EskDD_$o^1ICA}PCrOBSsFZI^c-m&x z{CNp4>HrnPvU{1|7$=!Em~WSFrdfqzKgpTnI!q+yN_guW2kGk|(p>vzy*+KgV{Zf< zR0jENCx)KXPeY2B<78YiSXe%fpaDrAnjA&-B_HD@C>)XPelfSw=pkWC#`SNJkd6UYXHws`!Es>1CcDG#|wr$2sl3f5@7(6O=un{qF)2(O2hIT`Sgzl4RA9QYs&G;rN$?`aI^-_k6eseRPe<(9r= zvUkHl!!xENj5FBy?z7&Nl~?9EZnQp2ZptPrwKKK(**;2NwK~&6v|Gl{i>Su)3e71B zP<`xOB_T3>?y%Xs8IoX;u(^L_2XWAGzuhV23hOQFqGaZbE7~MEn3Gl((sT2|HxpG} zfLOdP{wYE$B=IRh!=vHZL z1{-2PdM3bN@-yF2r?pGPZI${P8r1Agbq7-R$yXEI+@4+8^GDYn10B}C4%HtIXFXq8 zTC#e3hqc#2+~b4YLV9FkWz^@I*ByEcjE~4)sl{=#Yxg)6kdALHomB{_$c2HBMSi3f z?jLfc3! zfqil*(mJu7nxTrgY_5*h-2v`8`sP`Yft|!!ZWdwNY{kuT+;>Teb0p5aD3D~#F5ra%VBy- zo#8v3U9DJU{E8&emH?lYXhxq&vG?S-WOYEj%ASbu7oX;zO1G(SX6#~urTxT(?shNDzt8Av~F9ng4CNH{}7Ge0ILF$MK$v-T_J)hhn+G06kNx< z_4p{qv}(#WIt4jl`!s1=sb1n9oky?Wk3H7&%4$R=DO49*mQ1~XD`J*7$x%UmBQ65@ zg(`T5Aw>P2LsE^OKcU*5b$=*$xNEq?%?BkA#wA#L!U@}TCs`<8bMGn~)X!zrZL!8T{{A)*-sx$LbKi6d0 zujf>ur>v#p!F&l?;rIkbjln_3h4C9DvhmsM;!vN?jUMVuC~F zyj)gGd04oyK*d)=@P40|oNq%YpesSE*S#eYFPJ&uscf}og@xy?HpznI?gN(f1$^^R zT_MDFoyYyN&g?3aHJm~fuOqCD8rIv-p67k=ivMu$Gtq8IchOjie#cBC^vuYiOSkE2 zcLo)@A#T0RKM@f?t;mtOzoR4}xN z7DiKWDl|LTlsI(P^xJFs-Ehu%QRz}7zg;I^p+hczY>%Og^<^D&WL9YHAQ0LNl@Q`m zAsJHSo4Xrt(|&j6ePJ`e$9(jr}jxfC` z(Ysl-d7awx)uq(nDUOd9>_98z&!l)3iQy{)!VmmK5KS`p{*mXSUP^xU^>aDAKT|qt zG4jU<0$j=z$`w{b1St=fdKqfAe7-_u&(IVY;Dnek+2=I%^A*3~fy{Q6 zEcdStp&#r>o7FcsQzg3W-^x8^zsro53@K>PA$-8Q0zm-#vPpRt@e%Jy!unPPkYMiB zT>Ro4%7zpjHc4ut*^k9%T($~FRAsk9H`Kj3)T#4n-`Nwwje6|FH?z)CAo_Iu zZi?+hw}U9pDmTN@SjEm;Cckk^qt9at9}7hom-l^59(75+7Bas8>Mp-}>k1T<#ZFQ3 zTOGKs7RBXoP-8RZKl>8hV1Wecjd`)`#J*a~VU9*7wZBkJjGwGT8LLl615!DG@kf-T z*JEr~EV)d(W{B}OTQiJahsry&tgAhz6u)9c3iK$tS@wYa9mNQ9=vB5Mh#h|0!D23u zi`&3ivT+099wyihl&o3qN69o=RZZ_`+V$(3^EG3l04D|z_Wv~&R(-BR;S zmeJfHL#Y&^-r!0tEp>M3?__}4{zXn%g6SmBsT@1;t;m%2#ujNLU6 zPa4)+?;p&eRxTgr3v@rRro2WZGNUcMbn|Y{&AqR6Hbz^e#AWChg|rH5qLE{n!TRP( zcFmMR0h1TB{3Jv({)6?z+%KXkp9y?@`FuRE`K4Eyn+4Y_%|2fCKpKB#_c!(UZz;jy z@`f}P`9Q7)Vm8f#oxJ$;r8&t@$sBFcH(mLjH|H^0%$9JiS;s!S9d5&!zgvHCISJA0 z<;F_8X))8)8sj-%AGq_>x%6QNT}_COlxe%_Ecze}_S}5sx-3N#>a%*3yVu%++WTCJ z*+IV#T?LFJ^~UGkCD2yhz1R7;ZpCovF`vrx(LE*M8@Xknz}yXMzbk{XH{ADO(YGnp z6iIf<9$A~Gh2$!(-q7=$*Wjv|lV9c@HWsy160m$?$FR1z=HBGd!5TdEl?G2ekK>Wu zyCYg!f@B6$<$M=-*K&dM0f)4EP3yNUzasCxqKJbMXEH8*GfP4xcY!_sD&wwfy7JgG zt`1h~(B+}RfEZMLp!Fr8M6~!DSw3IKy42yeQbel0_o#D^n$v zN=8gc2U4XaHT^W4A*X&=stxp*rv zGI3vJ7QS}C#f#J5)bmJ_<|ys0<;5lez1b~+PI`36Jd3C!jzJNTEJu*BGY97*KY900mv2)MlC4+Rz??tI`;G);xY-aKp z;OmwG2wF)KVio-|?IeaBWDm`&s=Y6NFo(SB*Ar7Bi%YZbuj^s`cs`Gptguseo1-J) zxAj#+2@+bzo{ejtmusE~e5wEnqYgRxeOG|{<^@^2!Vw)5A%aj*zDthOT9twl`e>kD ztGSvOBw00;ID?>v6n-Nq$h1+a36jVg)^VX!z_o69rP5p%hst%ANk=28b}i{V?XF@p zZn(eC{2p$;3+J@0=IryyssRcw<;dq*8Uc|{qi1YQE@niMA&Iyu16ndSCAnX40?;ZJ zQP1LO3arKCzTm5Vp=lk55z%_t!d6RfYmD+lwFs{15(QnnD9P?wAI2jnn}rLf89YIgM27~lh3o2MSiZDa>G)9#RdO5PI@q!qz)-6lC>v~QrnLYR5E z6dX4odkZZbwy5Jox$Yj#|8<};flk+;8o2eo4L61ucVU2YYMMl=tq@EnKlORy>CT`n zWEgpqJC2W;MT^i>x#{7XYcE-Ew!%n)QFutOU&j|}pWZQx`DIGL!rnl`5Vr(AzMhnt zs;KqW-r;%2&vfU}wHEv_K2(DQc%Ha!zOeuDpMX!7$f;#2_ByH}R zs>1Vr8*@~bl`?$-xES4Ht!dyYxv`|Eiz{sXu_He6R-n3{kK$Pp^{|9m+nmWLfXb+Y zjae-`<4xXLtBlRidFy}x)9k?aT>v7{tH_AM)N!K8PMuMzy|Y1~q>e?Puh%+!Ga&

iN-ow8W!sChFbVwj)of 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 B0*L?s diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 8e19b410a1b15ff180f3dacac19395fe3046cdec..93248f4542880233a893efc4ba86a21e36d9facb 100644 GIT binary patch literal 22236 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

iN-ow8W!sChFbVwj)of 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 a9sdtN6eHRrdyqB&0000E4oWZ5i=uRa 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

iN-ow8W!sChFbVwj)of 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 4c19a13c239cb67b8a2134ddd5f325db1d2d5bee..93248f4542880233a893efc4ba86a21e36d9facb 100644 GIT binary patch literal 22236 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

iN-ow8W!sChFbVwj)of zabsgXoq77flYiGW<$+TXUFy6KC2&F8%C2%PV%(FXMBWbl>RqR6Z`e8UWHQC4Vt$Yy z4q=h^Ryqb23ltvYfpgrPc<4_rd&Z`|J&;=2xnB0M;N{nF+t*u1q%B0R5ABH%Vm;kX zfjObUT?vSUE#IZkiYP(5?8o#{##X!n? zxt|j1U_(2PsHAO{uC&7GagT3fG@)U#_(Ce97BBv+hu)dSKNe`aK$rUI-fkZ6POnHH zoJgHA7jein~$iG$k;ClW&9D9>j`*2tI4(qv9zN!JXwl=xzB0c; zhx%NSrAck&jC|phMDCk!FdQ@w5GeoRl(8w}Y9< z*4VWiH>r_R>&a~S>wAcZq%eI=?I0X>hdNG~m}G%|zv(Ctf1^<~eCspQWVv0uA9*0fjZTkj z!7nSIhBJ=ipMX%M?wV^7+9tDhIxo`?3N|O~OE;w2(B7zpvv)?cP)lrLsRYURZd>g= z`C&+hS@H!qfz;q=-{pj7B+5>QqfxQ-_~r85)dCx~$>VqHhpV+7(u%fubsHu0dM(m6 z(i5{L--~_0Mke`8`?-DJI0kklWn^95xc(3hHWxyDsy~qOP=x5{B-Qo?-@9bX zRk!L`Vv*MHA~(3toci1Tn`#GR@!eE#KFTs`+g)eA+hFq19c5|K+j4i(`#|2B-5mx< z)x%6MAi1dDsn2R+mk9l07zl+|+sNKO!(sN4Su?9aF#CW%0FWfa{F(8Nx0gB7mk$|; ze)Xr`*6;C-_-PUacgRKji6poU&lELkjTwy<(`y&K4-_m*v$)zS)riBK75pNddx+jm z;X-0aoT8yuNgYnTyEz^@8}{;v&LZ{LL%7#%yBE0}OYi9%1;n36fy z??@pwpW}scdaO*3-{e(|0(EHW(TZlVnUo9Ab;)$GtFj-*tH{HsC8F3ZB3fJSc6%&p zp?dYp=O=!9^Jl&dew`&+*ct1#mK#i)Z<<*|m)>sAORoa^!w@YH+xO>+O&oyle)D|! z%qjboCqW!}!ng;?jV|96aq;s3PjB-K=N55PGwm#q8Xf`m>1f|kJ9v#dtGVdTKPWPp zW7`>H$R3QRNKgs2tIGPi&AY~5buc^u%f1(K*z2~6zCL@)jb!dkkXKH%$s=E{5po81 zoLW@cNaiM6;2m*y>TU*Ni2~IXrO^0J=Azuj+-e}F(H?EWCjO>qta9voCt^QYyc7>&>jwJxZ(J6( zU8Jjn`XM{l=em7-aE+c@40@Y%8Ld9IJ(9Eicn)38PRX-1OKw(K$45n9M?L1HaD3?h zX2M4yBm#6ixwATDAJtgeS3K$vsERv||%$!L4$ z9H4TjTY3Np#oe8S$Rs9>-Gmipmu2hw~@!bBkE5Yv+XdT%%hg+B4 z0*8a+TJV3~s5?($_Wj=6f84CYsD9k6V~5^iV$vqn?vP6ahLk*!=A6BbPOP+(%nO&(%r2fA>H-( zz4z~1>*iYL?tRWZ_k8=?-?=ADTT_`3j}{LAK&YyspmTRd|F`47?v6Thw%7njTB|C^ zKKGc}$-p)u@1g1$=G5ziQhGf`pecnFHQK@{)H)R`NQF;K%92o17K-93yUfN21$b29 zQwz1oFs@r6GO|&!sP_4*_5J}y@1EmX38MLHp9O5Oe0Nc6{^^wzO4l(d z;mtZ_YZu`gPyE@_DZic*_^gGkxh<(}XliiFNpj1&`$dYO3scX$PHr^OPt}D-`w9aR z4}a$o1nmaz>bV)|i2j5($CXJ<=V0%{^_5JXJ2~-Q=5u(R41}kRaj^33P50Hg*ot1f z?w;RDqu}t{QQ%88FhO3t>0-Sy@ck7!K1c53XC+HJeY@B0BH+W}BTA1!ueRG49Clr? z+R!2Jlc`n)zZ?XWaZO0BnqvRN#k{$*;dYA4UO&o_-b>h3>@8fgSjOUsv0wVwlxy0h z{E1|}P_3K!kMbGZt_qQIF~jd+Km4P8D0dwO{+jQ1;}@_Weti;`V}a_?BkaNJA?PXD zNGH$uRwng<4o9{nk4gW z3E-`-*MB=(J%0*&SA1UclA>pLfP4H?eSsQV$G$t!uXTEio7TY9E35&?0M-ERfX4he z{_Hb&AE`T%j8hIZEp@yBVycpvW2!bHrfxbuu6>_i<^9@?ak)9gHU*#bS~}$sGY*Fi z=%P&i3aH%N`b;I~s8{&6uGo$>-`ukQ<8ri(6aH6p_F`Fhdi6HuacwfQn10HVL7Om1 z4aZpjatkbgjp$L5Mceab#G#C)Hr{^W|TJX~?B3@2buj0;kfuNTf4c3*Au~O^aj=W2$j^4okeCxh#lwexN@eam-u4dNz zN2NIuIM4566{T&^k%4ftShcPk#=im-zXm>QWqH^0>A@?MqlDZCZ@8Wi*@tvhn5p<} zRwFm@gz|WZp91S5Z{}tB^e9|FBg(~Ik+?&_53J6ye_QQOSJ*846~H%s#LD}|O9v9H z1fLrrgoPo_&bs}eqEr}2en3iqAcP^>YsKiez$5-6m6(#3ZZ$@M5Ck=_Vv`QA>1A*v z3w-nJ_;5Nc(0_%`kG91#sotIlhO!*5#|yg+Gx{V;0ty`*=Y9=jCh$l*=fE(~t}%R# zc}iNpO)OZX`P=leQY^?^DF1w%FJh>Dkp}-o5Ig|2!6^E>|W|zc~W7gF;MtxX7 zV~UjQNsUC$EYXpN?~o{83D2c*0~7;Tm~%FRTAnnt3ln{?DcLZ=NsBY|JxwUA-6K3V zP&#|9t#a}Q4{Sg{6v-OmjJBkCh>m)8vLNm4lStMUT$)FZeJG05A)px&o3H)5oAl9= z31@?HyCriHcCDnt628BFN+T;U69Wl#itfvqIDBydMvOJO0Zl?go$cfG5>TK75CMj3 zakLaH3=&J0e}Xmqlav$S0>E@_Yo_V~3SiiXrw)$&!XhrHCDQ%P1BHPusuKr0LthAB zg)mDrLy>2*yevMMOQe6fZ|)%PEb!lC^*9yaX9UMy7-v!fSICssTR|wML0Ic2BhKAq z3I1X~ z7^_!M&;6Z9?br3#HU_&kfJ~%botXQkC1v<}ZZxN5q-T)|Sb2cW3WYUBbDZ`TH{!*^ zrmAeRM+(QI>D+?}guZ+dH*X)@^!O|oL69&Avbtw2^M3HP(+2kV{O$^3BN1RLfrC8nwz7=VhBR%>!;7WR<~;34B_j3A{>^@e@H+Q! zL=UNr1(JvKAQLKT0b}EMn|QUWtY>!>8-t@fVj_&`~gGd{_aPy5W>0u5L$zrsU^rBO=i$`#Xd*>kh)lPf}A znNXSEl`+HlhXtylgS9(#N02A=zVV?#OF?)Gr>(HszVa+1*2VG@qYttJuXaBlzP`Pb zX)ueu?s&}R>xI#^*r4gR?tMFi!_eeKlIM5g)Nk)Y^h=ZCR**xY>$E5knctRrq!zw? zX{2|hwR9LXTY1)pTlKg7U4_ej{dcj2{!+1sZ6<@9^?mn)=37V)DIAvS(}S`IgFO!6 zn({?nYw`Z-@jvt@!q|5z?TI3(dx^1szSn%azAwp>N#fk^kt|=MejKtacAs@Rdku#zT>9$s z=m7ek)`=O7hO2n+2Uj$QUs&2EIqycF{(L9Y#^IyxXA%R@ z&j`VAprIV~d!pH-7~zA+bjwVn3kOB3;rlg{nr&wHV12N}g^i>Upls~=z`VX>9HQ#= zTu&luVb@_Lkz63&&^_M!6(-2^0?GCAX9XKp{O={pd|AlIMGriX6s_Jy8_q9|{5jLc zxd1aj_ucE7Vcti#$r!s~w~W=XpaLQ}#mX`apR7^n9-d3?O+adJYr*L;{c)x@REewM@vZN0njS3iE$88KHPWAkWt((OUMherUnPm?i&8@!9E@ zUW^$%CpdruZR0ohzUq-XQ$KEIB8Sjgs1+wKSUH&Y;=ee%E&O$X18{&979d~K2uJW` zd*8awHCXb;Q>4z$B|sPNv+Zd__f6&@KmS+L`z3H1x+x|Xs7-N-iw|1C=QiJdU)f~z z{vO4hpP`0MyqmwIHN=l?jSq>OKG6CEC#O`*blP`?>)CUWj5j1cB>%6N7;`kfZ1iQV zam~SDB?{uyp^=vF_u|=8xn3S)L;wF8ZRZV{bezM-EH;MC91JQZ{KcZZ$IWJUy?SJGeGUWm6PeuO8-K2|hD~p;Ls~9Y-4lE+?|bF)XaNKUNX(K7 zBQk0Z{n>hrH-CA`bTr$6z0n@Cn9EL$XZ3=X7NopjcI=;z<(X7-oEmK}BId=PxX*!b7Q6oL@ufd%eEPc`_la(}WkT zKe?-YJWn^6b$^{dhdJZ)I!Kn6c}iw%o5mLDyvM7qJZbkGG?zLU;M|W;Wis|A;SuY3{_X53`+>9g^B%O4b{;^t$^;{oKHbo*CY%u91 zp#2d8Pg=I0&UX{qwr=y=o_^BLdk=KYH$=Z8+k|p8V5`ph~3b^{^NnL4m_+4zx( zeoTt@f<$DmsB1}o%R1Hx`ToPuBl+P6cb-?uF{1!z-2WvdR4+vJ*SYTic5@gwnzu%e zD!HF^X=$ha^#1hi*@~^nDL!HQ;MC&e+6=onaJgm-J-+|>PpmU=SIe?EQE5vJiqziw z*K=Z%bWZz_we!qiFqE`I?#$yozNxIE7Ei;csv>++r*?)0bozFpF&oLh94u z-2c2L`5BarP7l>87|f)vxaT*9(!Q`2xBMZ&^JVj-|1)Tg!6OW=lk=w zLwVlr!*<(l*L$a?ox3+%!~UIj3Ej@KD;W>1E_c)1szDi93BC;0K?drOQ>@$yi|DtT zSir}!Yx>znf&b0KS;Lk7VKPDF@e>(qQr0%SNcGQd(p9StjqJ`QSW&c{ggF?5{d22w zlkX%JTUq`;(3WSH+)WHl%qlF)iNG_?}K?ZM3cS7#u5v zZ!apx4Apv=PWsn}eD%MI#=KA)OlNy0)l@~D^1;NC5k@|OPW3wt>WNYDN+8~+gM%E! z$ z`Olr0;eytiK&~O*ps%KV?2vq+DhuRh*!6Ilzu>A;iMe9 zI?zug9nT9CI_o)O}KF_I_U z_Cswu{)3pCYgw{eOt#E?UCqBwkAugSl>5 zX?G=Ci(Lo+r3suuJezyQyDvw*<1b{rx*&ZaY2HlJ>k{Qc%IZeU43pQXw4mh!4I5>l zZ@4$uxaPY#!*IhL4Hctn#!n#S+SiPcZP_PTd5fXf1exhFi5zf3kl`UcW2RUk)F2oF z_ogN`{03PiseQR;fa#{Uy;jeNlJ0Sle`~;ZYhLjkuy>a^!Z_nR~`$&F?NVuIE3HX;i zD82snwlwPb`7yE)ZA_Ndmq5zuSO1{{1}(d9u4#!Fl_|eOuxKBwOfQ*tG`VjCV$-WF zxi0c&+w}Z)rqz{%f46@`ADPdGm#x)+zpT+gyfDi;_P zR{#Ta`Mzd=putKO@5lQJO*aNy(i?}Ltwy^Z;69f|eqi#UCI1$vL!+(#mi?dK`OL$! z3jQnx$_$+Li2<__CL@Wuk4^J7-!n3j2I4N8e#=qpir+iEQcrn3`B4yNOd1BBLEni<(tdRWE>m0I^ zt(^*Td+S3}$5rOzXy=MW>%#MN_qy%5St!>HrGZ~Fq1WKw-&kv@2TrCcPCPzY%2aO- zN?7@+$4?&qA|uv{QHuV)O9haZpG7Jx2f%D)7J@oWTxJ#E_YSq_6qT1tomOD?02(1otT{Hk8{?g(944>h4f% zOJ8tzjecV{x2uWde&6oAP)*({ zFkW0Q%gdI*9@W)oKO65DgP<3F_BIKvRXLAR?Z61&0g2TR6mEZ7OZK?dP7zukdg?s_tNZeuOsh^e1Tmdlz5rIg?LcK|%aQ1FsSDv#W0EnHd z9M)p;gAL_R~Z5cojTdwy+qDsd6R01Vtxmq&FhfPz{wxmB$${zW~z@{Ro_ zK#y5^KqIp!#@or>GD`c+aZ(PV1=`Eo1?a55p6a*WepFgxvmp!^2518YEU-;{F}fLr zD~)=S0m=+px3TUN8-El}Xb}{2ET*_i3-|WlY@V7vr6#&cOr*+oS9?GF?@)K6op>>o z4af0@%KwaLr`{3P&)474<3rDMsd!IM-bepWfhfuMmJt}#0%PgDSx*q(s0m%ZFgWTj zwwvH%2!(i9{RHX~FVUB5qHvF{+ZF}+(bZVPG1)a*Ph>KV;cYNK^aB@R#dS~&`^60V zn2Z24Y{{djzK33}t@q%!v5k)u7jAXB_H{#4Ut2 z1}0j5$RXcTyfazqL9=^Qe%GL`G)=!lirv7AgVRf^=XyEM&kiOe_%JD!O?sXK&hrDo zF}m9B68im!oGshuZluy2H#T$`XPZQu@zf;(nBCZB-cjQ&w*p@Tm_$pe^MTN3EauI) zJG&G^H-4S|1OCd#@A6jO+IcAXG#5M-d9E!^YNmV7Z(=F^?8bfrYf&mLMnRd_22&Q} z2*msbLsrI!XPeOK@|V?n>`kNC`8eSFmekELLr|!-wQRltxZnuRedup<7VflowJ+gC z)F}P6lUSsh^B41?=~0*68YA6z63lKG`W$@{GV!cC2FCl0s<7yz6!3JWoBbUDTgpg% z4VNUk%xblMy7PjLF2We*3XY7K*N(*9Yx!_M zjU$&JXLiNxaTzoa&k@NSbzbLJTn$6bu6SPWYx)Zc1Li~Lqj($GuWsA#;zg85eH{yx zz3IIOea3A4QFGmJCfn7N_d$8a77j+T^W}Sr%0XdVLFf&zJ$s^D5Vrc!iV&GXyb5*A z6mG8d*6EDN7a;=dgVjYI--~4@Fe{{fcJ4B|;_Qg~&%6#?I(?X_$S4rDw{=>=8iZS=M^I#EF!m zXn%K_xXWwmm7R40LKXPo6ZzNZfN1-$S6RuVU=JlC|3#Xjo-%ebJvvC4n%IM)Q8NDh zGXd)L;ay_JMozc^mU*Uifnp=#+if>LD*O9MV#@wB1l``z|tlu(7PJqS6rm)0@ zJzP50{0Vpa`_?92oB;*i(?i225a6tZgT+9Dg?vTh)N4OKA~(c8{$8-ZKz=mb@$4IT9g8>;k11WIT+Y=%Z})`y#OJ zK-~rlEy!T%0h!Qo+jjPF2RQz2Z^B;dbvYg2JS`+@D~OWH{2-EEs^BdnuJskh>CKeT z1b;%8dU6QU%i@z?^6Q-{XESe^qRiw`ka+k!d-{c%&lXM}vCX^T=|?|;t6r?N*h-W4 z?o4Hy%BWqW+5=+md#5^8|49zjM zon_Do@rhzZ4XAb}-m|bMH$Vg<;^Bo6A8cfhUQ>|wFk~j(`>1NgD3sTg)He1pWrUj9WZ8R(Wn5Rr zhc&dXvv_m%HrwwHo9l_))NgdVUff%d&@4^$Pc=MDZdZ^xHL$KX^ z7W1{3UJ%>9v$W{Y3>vBvflE-soDj8{`>#F|8Z$EF%lN$NylORTn5JsI4mTMHWd*%- z2sD(RO(H-&i8&Ge)5i12slI5VekYCZ)s8rv&_)194;vKY2m8DIC2{4<&xTM3HHxwT zd(42n)gCJ$O4I|8sJq07#0U7Yk7PjPK&bMdy-5b)OdhSsBo^|IB_H43@&F@tpdJR0 z#~)=UJdP|=)O{0(rVZnjbTtwHV^}&kfLJQP@R6rda;K;O>9J9bnW$BgbzOZ8aO{D8 zPuJ%=Nqg~rdzk-IW0ZC5I%cc;ek5~=lDXl4?gMOQQ!KE5Aq$9qeGFM6jFP;Xy6)%N zjg{q(E6fnF02P3L*tutbHRR-gyYK3g^y9H?GMtIs;ojG zY~3*C>qD)(8jz}89w|xfb7L`^d>AG#%D-uq=qz}(o9kzzrx0LSBX90ykr*5oM+YmoTRWe+Cj6aq^xnWRymLmE>krCpoC9K%2LT0aK0Y< zt@kUUrrj1WL9rmBB8B;WXqg-BztOiUZX-!`*a&-75+!WZ!R0OPiZz?w`Of4q#+(;m z`${Ea6GnTCY3`V2R8w*}knf)*`RA@(8k{Lp4VP;<+ z9O_z0_{3=HcVi z5)&QGEB_&$)mu@)(Z8zuw#>Gc6C>^O-FUZEo;TO1@$>-xu%`v`tMS3V-8R1pb5w&zP%&rAP2*5h z$k{jqReFXCJhJ?-{x(2j5gH_zQ>;#Ec*@bUqF0u}XB09+U-K}+jQd>)k#AOkr6M8x zHyhrfJ`99@Vzr_B@*p@`DxeJ#`jimavZ9ZV%v{mO0!%9$TY(f%_}BU~3R%QxmSdD1 z2Bp45R0C=8qtx-~+oULrzCMHMof!&H<~~>BhOu9t%ti7ERzy&MfeFI`yIK^$C)AW3 zNQRoy0G}{Z0U#b~iYF^Jc^xOlG#4#C=;O>}m0(@{S^B2chkhuBA^ur)c`E;iGC9@z z7%fqif|WXh26-3;GTi8YpXUOSVWuR&C%jb}s5V4o;X~?V>XaR)8gBIQvmh3-xs)|E z8CExUnh>Ngjb^6YLgG<K?>j`V4Zp4G4%h8vUG^ouv)P!AnMkAWurg1zX2{E)hFp5ex ziBTDWLl+>ihx>1Um{+p<{v-zS?fx&Ioeu#9;aON_P4|J-J)gPF2-0?yt=+nHsn^1G z2bM#YbR1hHRbR9Or49U3T&x=1c0%dKX4HI!55MQv`3gt5ENVMAhhgEp@kG2k+qT|<5K~u`9G7x z?eB%b2B#mq)&K}m$lwDv|MU~=Y(D2jO{j*Box$GUn=$90z6O^7F?7pn=P;{r4C8qa zv1n*5N7uIvTn`8$>}(74>Oqk=E7){#pHUFd5XRJ5ObMhqODTa}=V0;+a(7JZR-4<3 zBTvsqRwLh?*ZF)JWsWOkEq7*XMQ!G3Rmkdh7ZbM#v1~?jt((e2y}u}Ky>1qa&Y7m@ zveIzH@?5Gexr79*?sbZGkVS;s1U<7D(%~7HjAmzj$aDYv_FGl5JX@LW8>w=HCDl6W z%?rsr0)bErYJ5G1v&zjr{8=lW)ZYcstgZAuL}!0~8HAcgOm@nJ9cvOOtL@)Fpl2Dr z8876Lt<|1eF88Jx#C*XyGI)C5z_o!Os!t=Xy0$Kj^4fG1pb@16%g z+<)zJ1n1QO78g#$3yHj+(Smv`HW5y_-PP{h2A1UXMG-c%hMvHLbF6t}G>KA)H# z`AWL~>8JUT(iq7;zJr!Aj)AS+n{mRbA3aM+Gj}b#PhHdTM_NkwQm330EC9waM$=slPfxR1vmr!vf~t_M?a%`@`&tdE}ipY-p#Q#zhLK zd9eFC;PjIEAKLkRkO94{rTuNFqKbNUGtaNZRRbax9;|%2WbnGu!44#64RriY5u0O} z05G^e&JB?Wb*8^g)aM`yt|}~QJkKCipFNeyex~P~SFPVEafD(73rncKmm)m~&`O*YUyY9z7tO%ec7z@wWcoOr-ebP z1k+|y?d{>1jLC=s4B2tEhiTtu->WVJno&%%6bG46KuU9D`GEN!C!9chM>zd=cl0+- z^k>4rpkq7_iWGHtBvy$Q`dja2;1ZdYmF6cANU6{v>l1=fSKRpsTRonp@alC%p{bhU z>g+(%-)&_nDQ~#bq5;xo^06RggA&uH4RMVb6wt;oQI+`m_zt>SiI5hXkfEnn6@ZNk zh9KUr1jtt6lBg$O#TAoTRvwUtWeMP3EjnGoRPQppiNF(sX%|Q4@kIjas|WZWXSENO zfF#2yOb;%XO*LeOoAwlf{u7_39$x(w3xT~)2BNJ2l5u4n3a0NkNLT4yT);7fA?1Vt zCz*`hbw-doYa09E!05zcfOT0EOORY``E@D z5{v%@F~&|UfNt@>vrj66W5f>jy+G_8&VB9D0*>N!7_Nr=-x6N?A)M8>1~q(X34sXp zpA%@w&c};L7u*G3;(Qe=LFL}NbTF$|aX#A%P(h`-N=ZRxCvlG$>Klv}jo0MS|UR8qKq-1FokBJmrbTJjQ!k#Is0tY+0c)m4Gp80YzYD zEGXd~ihaihk;?xUknXNH?rssjzaF+l6?HnDQjVP$i=q}{lp_WbOTKKg}HPKW)2sW`L#NvgmaY0^b2Ldk|t{P6{L{>ym;Xgao1PrudBgEMRFb^ zkPJ6v0h^tJ>K@;maHk_|6Z>yFzq@YvDOeO6Ob_?P4Ey>kHiJv`Wlh_MX4fBY36f%^ zV#2t;$Rg&}!Kwifm z;TVZXMxw3~$--{&A8-6vnUZ#s4`Z-zQ#+y7UI8#Hgsc|ompLUc zqlAG!Ti>t{JzYF^5pM925*PUWUvDuYDGKhC4FMx45c`L#V7%V+88@|khLj|V=J9Un zJEcP5qVCzR6p{FK!nIY~TXo)tJ!{>CG;~&u;EPlnNrwJ=5)ke@hJosN!siM$8b2mM zmc&weo-rY{n1+%c`c<{AT3i zjF{p253Ul-)s5A+!8Dp7?viXAdH1+qlY%mK5pp?{pS1t!3qmmDOq2TnoV`F3<>(XK z1=gfH39N_~8O+~({MZX~+QHyB>vtgwK0@uqGkX^eaf$UFHiO#>LB*7@=c0o6`0muj zmH00_F#p)s3E*$A-zP+p2bvXARTg3)Lxh`tf~9X>7!Z^kHV`uE%V9+BiBG=mxj*)M zr%3rn=)>GR`{#zmwD)$3ToLMx++uqsCx(+50Uk*5QJp2c6msxLD&P-y{c|XK6zZl3 z_Fgu8kp|gKVWv`GS!c56FWPO)ZrCCtYh#*yp-ssus)ot>_~UB zyGfjTjz#fXod{^KEQK1~@jN|;SZw5OgH#0wK78Oe4#vV3*|&XPQU z$r~5u8ziT0<#ICrX^<1){mvtaqT9OqlW?wiSu4X#rOC(0uL{Ownb%i1F_G&d>=l51 zx!FEO4_LK+)W^N6UF+fAccyyp{t)TE`;vF@1irbNjcXF8b?yFh zl5UEB>@;wO`~gMF!QB;h<``+f(lxAb_8B$;&vT7)(bXG(7x_5f%AZ5;h#3WjHisX{ zLTSguapAADXMwWZ&jsD0+K!+8#*6z7-(T+QUk>(~!Q|0&!d)PgEw8F6RK;LkB;!HXg79$+l*KU&-fRF|$o+kR4mJ36k9p&>*uS~RhCV+*Y$3U-k%~M)jxCFW zl9;bQ-fx4HPy)*(bhrKL!81M6*@6p5W?z*W`jb;@JKMFwmic{gQPv*) z?I{Fh)y)}(-6uh^I52xKo!LRZV0c*1X)Z(g+GVFN{2n%vD*@&IkVI{R_0;M28M z8vu?M+xVF-&<{l@1g{PA#hnyAq(gudz4WKSFL5YOr3q!|qrxa7z~F~rEJ29VQKgNe z1*L^m9&acg2p7&`u&V%oY|AKF(Xpv=)wf&j#n|;2UYEaUIHLJuTQw$SbrNn+)38PlfV^0<6s>)|hT#IAAS*T)_^_q@I} z0S%tV-HrXOjzkvW!YSbDjdH=g;=4A@whsDB zI8^aX6n=|ab(?!Ay!)CxH(wC(iX~Q@%FEx>C{Hmp98f2ku$Bsw%lk6v50(U@; zu68Z9U&za}O#-Mv^+!V=eyj6S)5oS{My`1MVs)nlnYl_$xU^QId1_jMf7&K8ij)jQ zJ|+~@l)xpV%~Y{P()$`+nBihkjE|3t3t8PoKU3wZ_Eg%0P<>%(A@oW#*8i$X!nfG& z;&&2ZIKlD~*Gff+p3A7QB!}Ei>RGhUUz^UoEpeJ{`2ov>wH!O@1$VW>A#D#{i2z9l z{d)FK9OYxRY#(6NUMO=q^5Ve7R|72%f}ZDlsm0BN&LzyaSHurXV4p5HGf7|Z)}8)g z5J#S6h{-+_U0m$k#+|N{6_8MYactWzWb+1~ea8wX3zX<@O0>pU*q($J{=R&7)P&jg z6Kb)o=HAnC_MP;cIeBq}{gG^0CZzOUJZ|7C-VjE}!?*UtKTcwwF33v^BYC&}Rq)C* zpAJ07-!{`flYX1@n;ZK-=x4)!o(%(1UqulVmes(D z^`_HNfM#umEYy~=zh$9&+?8$4!l(4rr?d#8hS4iks@9w%E4l`BKmhUtvsm1X-mKC3 z>4(u4yS45OgZIOQ;EQ6s`sjNelo!~mLe7gS69TW2WnFwEKcAwioq2mLXV<9CIa#(0`sQpl>vwW`A$D?!2%nt*HEb;Ga=o?92 zHAOICmXHEQ%Cc{m2>dLjPU1J}^w7zilFIxy9nG(OZbYPtW?3KJyv@A7|1A*NiD_v! zTLC}%E4kI*d?$lQBRL==MPsD#FyN0ZSr`;aeQ4C6a2INH9klU~_gCH;G2%8R4EuHb z44Ej^6301>?c06FP3X~xyP{77p`-3td;HKAGf4mZw1qRd6Z^^L#?qaiAKv~px)*jAV^re~beps9m{kJzb6n(oS8uCt#Lnjofg;Rl z=apY)JsV;^dVkzCW)jDrii_WTT`3iKri(xmCC1^AO}Vqt-1B*wwIlBAmE1AmdRtMc zD!fB@mtwHPHyV-^VIVU??*~*{olz-Ub)NCX941BDj_CKZ+QYQ?+``tyhy_7WFXF}_ z?~CVO#LsDYD!&}cph22{PZ*TK?$K^u`E7%{^na89Rm%!jSZs7vI-D zL1POD!1cu56G)*p1gui3-i^JZPX3tI*_Fq&JRwbz*#8LUSiMRWjuu`zD|uk;+X&d@ zuxF5C2{Zp#O?GtOB+R2~tF>MDI(}%p-W=M>1tEY}8E=b_l*WbOO zY9tCPgL3vMEqz)_eWeqmN{qobq_4)XdXJSe6Hj;Eie0??2ZZ?p;*_K8@(&v~1evu- zxQCA2YYvv@qhzamqdi`?{Z{c*7$arCdz4-4G(`O5It%y&8>d{#Y9Vax^FZ99ZK zUdIPpkNhp8uP3T+W4lhvUIYaoY##y6KtxBFoj3&5^@Q(^{677%C#3YJh$p-Ee2M6F ztJAoQv1N0L!|N8XBD(eAYcB#gRaIX7T8U5xXbx~cJSon~YnC zaJYE%zOj9y?E==_B$*9NiAm{~)2Z}t1$$l?qOYct5Ep5HvqFKvuSE7A5YF$K@2>UE zbQOdTNzjD#zS(L>wa2$K-WK!Pc%pY^8To58;^JaXZ}F30wuYl;WWs~rCoo&vrEtUh zTBLMU??yx1#;-weCPZyOJ%Yeb?14z+OXW0L_E+<)(q=;xz74U-Q~R~n*oC;MxyrJo(74r$y2t;x`D~{nhUw`N{Bbc zo`l5kb`Yy;L=&@MTQ~Ml_%V%){mCIj4WC}5q=A_ACx2^by!4w1rVX6H0ifayJsw;; z=+}5kjC?RG*q)^FA;udd?fK$7vU1x>y0w;A-)YbE%l$J%nRRjAIlrItFPgQvJ7Ytb z%HSFnjF2||X&L_g-Q>1{(mholW_-EJmSzsO%*VVVB4)#OAv<(kOIx2H!f)I9#e_Nyjdb$&*1KN^gM}yFIhi%%BWB}7Ke0M{0WY>CxJQUuL<9GW$I>S z8~;QmE{^wS?I`=DyV^l+MozMPWLoFz=uSLu99tiVHdCN>7jRs~vd13`&Gey!!7_+< z6o@25%!eN~+Eki#7iq@#{Hxl7pF0^`N;~p~#tc6HXJP0g5xvK|AuLSwNHVI2_Y-!& z4hemc%vOM5!ySDypyEGe=lAeFbIp`w8FIUcTqUwens>sTIV-jDhrcKGX7XHFXyazb z^DO8=ZgefY6R6&+)c1_i*WoenjtR5@_JU#Ph;4M8fpmznxE9R`=r@-#_y zkD?Muq|*gg7f*BQeI|Np#}Q|NXLJHM6GE{;SJn8ce`V1Gehym~{8c+M<2~=HcCRuk z-v&$8dc8YG+tK}NYVhwdm1iZ&A#r+T<>Ez88)Eq9j+G5h5D(_u{WQdUTOs+QbA(=? z{F6n6UV8D2*lvb)0vDrca$729KG$xO2aH$jWoWl0drlmefYsTswh)`GjMtmR=vEkJ zN$aTp_@@KL%KQ-VDB2ppbZK@X`6cJA5n`g>sbCTvU_xdid!{9gWA|>Mfs6rtHx6s` z_wMt*FgUTBZ@I2C62&zbs?pPvK9TpatkXzqDqe4YTr^nnQg8gWxjKt*s&eOMEp!Qc zG~PT`>xg76Xqh^dKI-Eu#K*VnvEf9qT{L0yNpVj)eVD#kQzGgVRbTB!5nWY=?t!cggiEGBAcWM2xNtW&9 zZB_6RZ}|a87CuEYRYCRJ`Sg+_gBK$_J@*zoWcJJw>eBw?G9WY(Jw~qN|A3MBR^~jm?>k5oGv7z+0jWOox(co@%nya|* zE-2peyX)#@svgwwDMPJ89dT=iO>}@wtNR@NUQ|cJZ};sX(w2uWP4AE5)@A ziJgy_TIZ+T&vG&xPh@Jmt!OJ|zA6C0ZxfF2 z7>aIZqecbmM$lyvDMwg2?Ipo9b)-WL6K_7(X_rmJgdd$-Qc^ywEw4SThChz6*_yu= z{v~a4V|RJtH-GThc2C0Z|JHPl{II-!?B~7cWnRz&dgP*UqoY!iCo&i-xeM}kl?ID* zKTX`w+;z0+MCdGcl{N?xb|tYb%Id=k++k_@(V%bTS&n09`0{S0)|>IH_F;V@_zrxS-dKDDc7+i`nHN8J z;38w69lzAS*WWa+dnVvk(0-KD3%*)TerLH zSCc}Tjc-mR5|1HAL$C1}oue|Qp&M!hmyDUcg)Cz>GXPEyeYf}+s48kIl*pL{{treP BIP(Ai diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..fd67559 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + + #000000 + #ffffff + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index f8711ef..ad777aa 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ + E_PSonWckd1Pyhp-6okocqPLw21a10f04d24-92c2-41a5-84ae-b434a9f20a91 + Github_RN diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 319eb0c..e46695f 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -3,6 +3,8 @@ diff --git a/android/build.gradle b/android/build.gradle index 3231b29..58b1cea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,6 +25,7 @@ allprojects { mavenLocal() google() jcenter() + maven { url 'https://dl.bintray.com/umsdk/release' } maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url "$rootDir/../node_modules/react-native/android" diff --git a/android/gradle.properties b/android/gradle.properties index 89e0d99..ceae311 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -16,3 +16,7 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +MYAPP_RELEASE_STORE_FILE=my-release-key.keystore +MYAPP_RELEASE_KEY_ALIAS=my-key-alias +MYAPP_RELEASE_STORE_PASSWORD=172529131 +MYAPP_RELEASE_KEY_PASSWORD=172529131 \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 0d84105..bfc4855 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,7 +1,12 @@ rootProject.name = 'Github_RN' +include ':react-native-webview' +project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') +include ':react-native-splash-screen' +project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android') include ':react-native-vector-icons' project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') include ':react-native-gesture-handler' project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') include ':app' + diff --git a/ios/Github_RN.xcodeproj/project.pbxproj b/ios/Github_RN.xcodeproj/project.pbxproj index 376dd2a..db46149 100644 --- a/ios/Github_RN.xcodeproj/project.pbxproj +++ b/ios/Github_RN.xcodeproj/project.pbxproj @@ -58,6 +58,10 @@ 466AB0B4C8E04AAAAF7BBE4A /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A7AE27456DC64EB49FCC2DC3 /* Octicons.ttf */; }; 51463D0D71484EE184ABBCCD /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5A353461451E4469B286C768 /* SimpleLineIcons.ttf */; }; D4D8A2191CE3401A9B32DD65 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7130809C8BA946E783340944 /* Zocial.ttf */; }; + 92EEB262A0B74CB7996BD838 /* libSplashScreen.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A60B592A6A742FE9EB23F86 /* libSplashScreen.a */; }; + D1988C364F694AD8BF35CCA9 /* libCodePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 514258C52E324F0D848232D4 /* libCodePush.a */; }; + 3BEF98B363F94BBEBD61279F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 470262C5B1BF43C79C911498 /* libz.tbd */; }; + 926576E1EBCA480BB45C2B35 /* libRNCWebView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E996E96A7CD741309BF0E785 /* libRNCWebView.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -386,6 +390,13 @@ A7AE27456DC64EB49FCC2DC3 /* Octicons.ttf */ = {isa = PBXFileReference; name = "Octicons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; 5A353461451E4469B286C768 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; name = "SimpleLineIcons.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; 7130809C8BA946E783340944 /* Zocial.ttf */ = {isa = PBXFileReference; name = "Zocial.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + 1EF9F2201C9E4A23A7C4DAC7 /* SplashScreen.xcodeproj */ = {isa = PBXFileReference; name = "SplashScreen.xcodeproj"; path = "../node_modules/react-native-splash-screen/ios/SplashScreen.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + 0A60B592A6A742FE9EB23F86 /* libSplashScreen.a */ = {isa = PBXFileReference; name = "libSplashScreen.a"; path = "libSplashScreen.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + 8B7304002B7D42D28C983932 /* CodePush.xcodeproj */ = {isa = PBXFileReference; name = "CodePush.xcodeproj"; path = "../node_modules/react-native-code-push/ios/CodePush.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + 514258C52E324F0D848232D4 /* libCodePush.a */ = {isa = PBXFileReference; name = "libCodePush.a"; path = "libCodePush.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + 470262C5B1BF43C79C911498 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; }; + 930571BCC2BD43D19D3A1A00 /* RNCWebView.xcodeproj */ = {isa = PBXFileReference; name = "RNCWebView.xcodeproj"; path = "../node_modules/react-native-webview/ios/RNCWebView.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + E996E96A7CD741309BF0E785 /* libRNCWebView.a */ = {isa = PBXFileReference; name = "libRNCWebView.a"; path = "libRNCWebView.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -416,6 +427,10 @@ 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 29A3648EA1C845C9A74C1C28 /* libRNGestureHandler.a in Frameworks */, AFFD6DB7BFCC490CA8CDB584 /* libRNVectorIcons.a in Frameworks */, + 92EEB262A0B74CB7996BD838 /* libSplashScreen.a in Frameworks */, + D1988C364F694AD8BF35CCA9 /* libCodePush.a in Frameworks */, + 3BEF98B363F94BBEBD61279F /* libz.tbd in Frameworks */, + 926576E1EBCA480BB45C2B35 /* libRNCWebView.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -570,6 +585,7 @@ ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, 2D16E6891FA4F8E400B85C8A /* libReact.a */, + 470262C5B1BF43C79C911498 /* libz.tbd */, ); name = Frameworks; sourceTree = ""; @@ -609,6 +625,9 @@ 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 79F9A0ABB9F6485590DB18A2 /* RNGestureHandler.xcodeproj */, 8309BB063EB945DD9FAFD3E4 /* RNVectorIcons.xcodeproj */, + 1EF9F2201C9E4A23A7C4DAC7 /* SplashScreen.xcodeproj */, + 8B7304002B7D42D28C983932 /* CodePush.xcodeproj */, + 930571BCC2BD43D19D3A1A00 /* RNCWebView.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -1279,11 +1298,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Debug; @@ -1309,11 +1334,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Release; @@ -1338,6 +1369,9 @@ "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Debug; @@ -1361,6 +1395,9 @@ "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Release; @@ -1394,11 +1431,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Debug; @@ -1432,11 +1475,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Release; @@ -1469,11 +1518,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Debug; @@ -1506,11 +1561,17 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)\..\node_modules\react-native-gesture-handler\ios/**", "$(SRCROOT)\..\node_modules\react-native-vector-icons\RNVectorIconsManager", + "$(SRCROOT)\..\node_modules\react-native-splash-screen\ios", + "$(SRCROOT)\..\node_modules\react-native-code-push\ios\CodePush/**", + "$(SRCROOT)\..\node_modules\react-native-webview\ios", ); }; name = Release; diff --git a/ios/Github_RN/AppDelegate.m b/ios/Github_RN/AppDelegate.m index 8a3c64e..bcdfd45 100644 --- a/ios/Github_RN/AppDelegate.m +++ b/ios/Github_RN/AppDelegate.m @@ -6,6 +6,7 @@ */ #import "AppDelegate.h" +#import #import #import @@ -16,7 +17,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { NSURL *jsCodeLocation; - jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + + #ifdef DEBUG + jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + #else + jsCodeLocation = [CodePush bundleURL]; + #endif RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"Github_RN" diff --git a/ios/Github_RN/Info.plist b/ios/Github_RN/Info.plist index 0fb44b2..aab6ed9 100644 --- a/ios/Github_RN/Info.plist +++ b/ios/Github_RN/Info.plist @@ -71,5 +71,7 @@ SimpleLineIcons.ttf Zocial.ttf + CodePushDeploymentKey + deployment-key-here diff --git a/js/common/NavigationBar.js b/js/common/NavigationBar.js index 9757813..58aec31 100644 --- a/js/common/NavigationBar.js +++ b/js/common/NavigationBar.js @@ -1,10 +1,11 @@ import React, { Component } from 'react'; -import { ViewPropTypes, Text, StyleSheet, View, StatusBar, Platform } from 'react-native'; +import { ViewPropTypes, Text, StyleSheet, View, StatusBar, Platform, DeviceInfo } from 'react-native'; import {PropTypes} from 'prop-types'; const NAV_BAR_HEIGHT_IOS = 44; // 导航栏在IOS中的高度 const NAV_BAR_HEIGHT_ANDROID = 50; // 导航栏在Android中的高度 -const STATUS_BAR_HEIGHT = 20; // 状态栏的高度 +// 状态栏的高度 +const STATUS_BAR_HEIGHT = DeviceInfo.isIPhoneX_deprecated ? 0 : 20; // 设置状态栏所接受的属性 const StatusBarShape = { diff --git a/js/common/SafeAreaViewPlus.js b/js/common/SafeAreaViewPlus.js new file mode 100644 index 0000000..2f679fa --- /dev/null +++ b/js/common/SafeAreaViewPlus.js @@ -0,0 +1,67 @@ +import React, { Component, } from 'react' +import { DeviceInfo, SafeAreaView, StyleSheet, View, ViewPropTypes } from 'react-native' +import PropTypes from 'prop-types' + +export default class SafeAreaViewPlus extends Component { + static propTypes = { + ...ViewPropTypes, + topColor: PropTypes.string, + bottomColor: PropTypes.string, + enablePlus: PropTypes.bool, + topInset: PropTypes.bool, + bottomInset: PropTypes.bool, + } + + static defaultProps = { + topColor: 'transparent', + bottomColor: '#f8f8f8', + enablePlus: true, + topInset: true, + bottomInset: false, + } + + // 顶部安全区域 + renderTopArea = (topColor, topInset) => { + return !DeviceInfo.isIPhoneX_deprecated || !topInset ? null + : + } + + // 底部安全区域 + renderBottomArea = (bottomColor, bottomInset) => { + return !DeviceInfo.isIPhoneX_deprecated || !bottomInset ? null + : + } + + // 系统安全区域 + renderSafeAreaView = () => { + return + {this.props.children} + + } + // 自定义安全区域 + renderSafeAreaViewPlus = () => { + const { children, topColor, bottomColor, topInset, bottomInset } = this.props + return + {this.renderTopArea(topColor, topInset)} + {children} + {this.renderBottomArea(bottomColor, bottomInset)} + + } + + render (): React.ReactNode { + const { enablePlus } = this.props + return enablePlus ? this.renderSafeAreaViewPlus() : this.renderSafeAreaView() + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + topArea: { + height: 44, + }, + bottomArea: { + height: 34, + } +}) \ No newline at end of file diff --git a/js/page/DetailPage.js b/js/page/DetailPage.js index 5f19e28..c1c1d2e 100644 --- a/js/page/DetailPage.js +++ b/js/page/DetailPage.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; -import {StyleSheet,DeviceInfo, WebView, TouchableOpacity, Text, View} from 'react-native'; +import {StyleSheet,DeviceInfo, TouchableOpacity, Text, View} from 'react-native'; +import { WebView } from 'react-native-webview'; import FontAwesome from 'react-native-vector-icons/FontAwesome'; import NavigationUtil from '../navigator/NavigationUtil'; import BackPressComponent from '../common/BackPressComponent'; @@ -8,6 +9,7 @@ import FavoriteDao from '../expand/dao/FavoriteDao'; import NavigationBar from '../common/NavigationBar'; import ViewUtil from '../util/ViewUtil'; import {connect} from 'react-redux'; +import SafeAreaViewPlus from '../common/SafeAreaViewPlus'; const TRENDING_URL="https://github.com/" type Props = {}; @@ -108,7 +110,9 @@ export default class Detail extends Component { rightButton={this.renderRightButton()} /> return ( - + {navigationBar} this.webView = webView} @@ -116,7 +120,7 @@ export default class Detail extends Component { onNavigationStateChange={(e) => {this.onNavigationStateChange(e)}} source={{uri:this.state.url}} /> - + ); } } diff --git a/js/page/FavoritePage.js b/js/page/FavoritePage.js index 26f6b98..2f7ab79 100644 --- a/js/page/FavoritePage.js +++ b/js/page/FavoritePage.js @@ -88,7 +88,7 @@ export default class Favorite extends Component { } } )); - return + return {navigationBar} diff --git a/js/page/Homepage.js b/js/page/Homepage.js index 402f471..8584bc8 100644 --- a/js/page/Homepage.js +++ b/js/page/Homepage.js @@ -4,6 +4,8 @@ import NavigationUtil from '../navigator/NavigationUtil'; import DynamicTabNavigator from '../navigator/DynamicTabNavigator'; import BackPressComponent from '../common/BackPressComponent'; import CustomTheme from '../page/CustomTheme'; +import SafeAreaViewPlus from '../common/SafeAreaViewPlus'; +import SplashScreen from 'react-native-splash-screen' // 处理安卓的物理返回键 import {BackHandler} from 'react-native'; @@ -15,7 +17,8 @@ type Props = {}; @connect( state=>({ nav: state.nav, - customThemeViewVisible: state.theme.customThemeViewVisible + customThemeViewVisible: state.theme.customThemeViewVisible, + theme: state.theme }), { onShowCustomThemeView: actions.onShowCustomThemeView @@ -28,6 +31,7 @@ export default class Home extends Component { } componentDidMount () { // 注册物理返回键的监听 + SplashScreen && SplashScreen.hide() this.backPress.componentDidMount(); } componentWillUnmount() { @@ -61,12 +65,15 @@ export default class Home extends Component { render() { // 内层的navigator往外层的navigator跳转的时候无法跳转, // 这个时候可以在内层保存外层的navigation + const {theme} = this.props; NavigationUtil.navigation = this.props.navigation; // 给导航管理类添加静态属性 return ( - + {this.renderCustomThemeView()} - + ) } } diff --git a/js/page/Mypage.js b/js/page/Mypage.js index e596709..f016eb3 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -26,7 +26,7 @@ export default class My extends Component { case MORE_MENU.Tutorial: RouteName = 'WebViewPage'; params.title = '教程'; - params.url = 'https://ml.66jingcai.cn/' + params.url = 'https://github.com/songdongdong123' break; case MORE_MENU.About: RouteName = 'AboutPage'; diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index 01021d7..479d3de 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -8,7 +8,9 @@ */ import React, {Component} from 'react'; -import {StyleSheet,DeviceInfo, ActivityIndicator, View, Text, FlatList, RefreshControl } from 'react-native'; +import {StyleSheet,DeviceInfo, ActivityIndicator, +TouchableOpacity, +View, Text, FlatList, RefreshControl } from 'react-native'; import Toast from 'react-native-easy-toast'; import {connect} from 'react-redux'; import actions from '../redux/action'; @@ -23,6 +25,7 @@ import FavoriteUtil from '../util/FavoriteUtil'; import PopularItem from '../common/PopularItem'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; +import Ionicons from 'react-native-vector-icons/Ionicons'; //用于页面之间通讯 import EventTypes from '../util/EventTypes'; @@ -76,6 +79,26 @@ export default class Popular extends Component { }); return tabs; } + renderRightButton () { + const {theme} = this.props; + return { + NavigationUtil.goPage({theme}, 'SearchPage') + }} + > + + + + + } render() { // 获取标签 const {keys, theme} = this.props; @@ -89,6 +112,7 @@ export default class Popular extends Component { title={'最热'} statusBar={statusBar} style={{backgroundColor: theme.themeColor}} + rightButton={this.renderRightButton()} /> const TabNavigator = keys.length ? createAppContainer(createMaterialTopTabNavigator( @@ -113,7 +137,7 @@ export default class Popular extends Component { lazy: true } )):null; - return + return {navigationBar} {/* TabNavigator存在,渲染标签 */} {TabNavigator&&} diff --git a/js/page/SearchPage.js b/js/page/SearchPage.js index 838c92c..9e3dbb8 100644 --- a/js/page/SearchPage.js +++ b/js/page/SearchPage.js @@ -2,6 +2,8 @@ import React, {Component} from 'react'; import {StyleSheet, DeviceInfo, Platform, + TextInput, + TouchableOpacity, ActivityIndicator, View, Text, FlatList, RefreshControl } from 'react-native'; import Toast from 'react-native-easy-toast'; import {connect} from 'react-redux'; @@ -15,6 +17,7 @@ import { import NavigationUtil from '../navigator/NavigationUtil'; import FavoriteUtil from '../util/FavoriteUtil'; import PopularItem from '../common/PopularItem'; +import ViewUtil from '../util/ViewUtil'; import BackPressComponent from '../common/BackPressComponent'; // 自定义顶部导航组件 import NavigationBar from '../common/NavigationBar'; @@ -25,7 +28,8 @@ import EventBus from 'react-native-event-bus'; // import LanguageDao, { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; -import GlobalStyles from '../res/styles/GobalStyles' +import GlobalStyles from '../res/styles/GobalStyles'; +import Utils from '../util/Utils'; // 顶部导航tab标签配置 const TAB_NAMES = ['Java', 'Android', 'Ios', 'React', 'React-Native', 'PHP']; @@ -66,11 +70,11 @@ export default class Search extends Component { loadData = (loadMore) => { const { onLoadMoreSearch, onSearch, search, keys } = this.props if (loadMore) { - onLoadMoreSearch(++search.pageIndex, pageSize, search.items, this.favoriteDao, keys, callback => { + onLoadMoreSearch(++search.pageIndex, PAEG_SIZE, search.items, this.favoriteDao, keys, callback => { this.toast.show('没有更多了') }) } else { - onSearch(this.inputKey, pageSize, this.searchToken = new Date().getTime(), this.favoriteDao, keys, message => { + onSearch(this.inputKey, PAEG_SIZE, this.searchToken = new Date().getTime(), this.favoriteDao, keys, message => { this.toast.show(message) }) } @@ -79,7 +83,7 @@ export default class Search extends Component { const { onSearchCancel, onLoadLanguage } = this.props onSearchCancel()//退出时取消搜索 this.refs.input.blur() - NavigationUtil.goBack(this.props.navigation) + NavigationUtil.goBack({navigation: this.props.navigation}) if (this.isKeyChange) { onLoadLanguage(FLAG_LANGUAGE.flag_key) } diff --git a/js/page/TrendingPage.js b/js/page/TrendingPage.js index 8e1686d..b4d6855 100644 --- a/js/page/TrendingPage.js +++ b/js/page/TrendingPage.js @@ -178,7 +178,7 @@ export default class Trending extends Component { /> // 顶部标签组件 const TabNavigator = languages.length?this._tabNav():null; - return + return {navigationBar} {TabNavigator&&} {this.renderTrendingDialog()} diff --git a/js/page/WebViewPage.js b/js/page/WebViewPage.js index 0c2fbfb..78c2ce2 100644 --- a/js/page/WebViewPage.js +++ b/js/page/WebViewPage.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; -import {StyleSheet,DeviceInfo, WebView, TouchableOpacity, Text, View} from 'react-native'; +import {StyleSheet,DeviceInfo, TouchableOpacity, Text, View} from 'react-native'; +import { WebView } from 'react-native-webview'; import FontAwesome from 'react-native-vector-icons/FontAwesome'; import NavigationUtil from '../navigator/NavigationUtil'; import BackPressComponent from '../common/BackPressComponent'; diff --git a/js/page/WelcomePage.js b/js/page/WelcomePage.js index e79add9..3133e7c 100644 --- a/js/page/WelcomePage.js +++ b/js/page/WelcomePage.js @@ -1,11 +1,13 @@ import React, {Component} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import NavigationUtil from '../navigator/NavigationUtil'; - +import SplashScreen from 'react-native-splash-screen' +// import AnalyticsUtil from '../util/AnalyticsUtil'; type Props = {}; export default class Welcome extends Component { componentDidMount() { this.timer = setTimeout(() => { + SplashScreen.hide(); NavigationUtil.ResetToHomePage(this.props) }, 200); } @@ -13,11 +15,7 @@ export default class Welcome extends Component { this.timer&&clearTimeout(this.timer) } render() { - return ( - - WelcomePage - - ); + return null; } } diff --git a/js/page/about/AboutCommon.js b/js/page/about/AboutCommon.js index b228b78..bf35923 100644 --- a/js/page/about/AboutCommon.js +++ b/js/page/about/AboutCommon.js @@ -1,5 +1,5 @@ import React from 'react'; -import {View, Image, Text, Dimensions, Platform, StyleSheet} from 'react-native'; +import {View, Image, Text, Dimensions, Platform, StyleSheet, DeviceInfo} from 'react-native'; import BackPressComponent from '../../common/BackPressComponent'; import NavigationUtil from '../../navigator/NavigationUtil'; import config from '../../res/data/github_app_config.json'; @@ -125,7 +125,8 @@ export default class AboutCommon { const window = Dimensions.get('window'); const AVATAR_SIZE = 90; const PARALLAX_HEADER_HEIGHT = 270; -const STICKY_HEADER_HEIGHT = (Platform.OS === 'ios') ? GobalStyles.nav_bar_height_ios + 20 : GobalStyles.nav_bar_height_android; +const TOP = (Platform.OS === 'ios') ? 20 + (DeviceInfo.isIPhoneX_deprecated?24:0):0; +const STICKY_HEADER_HEIGHT = (Platform.OS === 'ios') ? GobalStyles.nav_bar_height_ios + TOP : GobalStyles.nav_bar_height_android; const styles = StyleSheet.create({ container: { @@ -142,7 +143,8 @@ const styles = StyleSheet.create({ stickySection: { height: STICKY_HEADER_HEIGHT, // width: 300, - justifyContent: 'center' + justifyContent: 'center', + paddingTop: TOP, }, stickySectionText: { color: 'white', @@ -156,7 +158,7 @@ const styles = StyleSheet.create({ right:0, bottom:0, paddingRight: 8, - paddingTop: (Platform.OS === 'ios')?20:0, + paddingTop: TOP, flexDirection: 'row', alignItems: 'center', justifyContent:'space-between' diff --git a/js/redux/action/ActionUtil.js b/js/redux/action/ActionUtil.js index 7c85f88..0a7fc57 100644 --- a/js/redux/action/ActionUtil.js +++ b/js/redux/action/ActionUtil.js @@ -8,7 +8,7 @@ import ProjectModel from '../../model/ProjectModel'; * @param {*} data * @param {*} pageSize */ -export default function handleData (actionType, dispatch, storeName, data, pageSize, favoriteDao) { +export default function handleData (actionType, dispatch, storeName, data, pageSize, favoriteDao, params) { let fixItems = []; if (data && data.data) { if (Array.isArray(data.data)) { @@ -27,7 +27,8 @@ export default function handleData (actionType, dispatch, storeName, data, pageS items: fixItems, projectModels: projectModels, storeName, - pageIndex: 1 // 初始页 + pageIndex: 1, // 初始页 + ...params }) }) } diff --git a/js/redux/action/search/index.js b/js/redux/action/search/index.js index 843702b..9b9d835 100644 --- a/js/redux/action/search/index.js +++ b/js/redux/action/search/index.js @@ -1,6 +1,7 @@ import Types from '../action_types'; -import { _projectModels, doCallBack, handleData } from '../ActionUtil'; -import ArrayUtil from '../../../util/ArrayUtil' +import handleData, { _projectModels, doCallBack } from '../ActionUtil'; +import ArrayUtil from '../../../util/ArrayUtil'; +import Utils from '../../../util/Utils'; const API_URL = 'https://api.github.com/search/repositories?q=' const QUERY_STR = '&sort=stars' @@ -33,9 +34,9 @@ export function onSearch (inputKey, pageSize, token, favoriteDao, popularKeys, c doCallBack(callBack, `没找到关于${inputKey}的项目`) return } - let items = res.items + let items = res.items; handleData(Types.SEARCH_REFRESH_SUCCESS, dispatch, '', { data: items }, pageSize, favoriteDao, { - showBottomButton: checkKeyIsExist(popularKeys, inputKey), + showBottomButton: !Utils.checkKeyIsExist(popularKeys, inputKey), inputKey }) }).catch(err => { @@ -108,17 +109,7 @@ function hasCancel (token, isRemove) { return false } -/** - * 检查key是否存在于keys中 - * @param keys - * @param key -*/ -function checkKeyIsExist (keys, key) { - for (let i = 0, l = keys.length; i < l; i++) { - if (key.toLowerCase() === keys[i].name.toLowerCase()) return true - } - return false -} + /** * 检查该Item是否被收藏 diff --git a/js/redux/reducer/search/index.js b/js/redux/reducer/search/index.js index 3a9c45d..6000a3b 100644 --- a/js/redux/reducer/search/index.js +++ b/js/redux/reducer/search/index.js @@ -20,6 +20,7 @@ export default function onAction (state = defaultState, action) { showText: '取消' } case Types.SEARCH_REFRESH_SUCCESS://获取数据成功 + console.log(action) return { ...state,//原始数据 isLoading: false, diff --git a/js/res/styles/GobalStyles.js b/js/res/styles/GobalStyles.js index 600bb00..3685743 100644 --- a/js/res/styles/GobalStyles.js +++ b/js/res/styles/GobalStyles.js @@ -4,7 +4,9 @@ * @author Ethan * @date 2019-03-27 14:15:32 */ +import { Dimensions } from 'react-native' const BACKGROUND_COLOR = '#F3F3F4'; +const { height, width } = Dimensions.get('window') export default { line: { height: 0.5, @@ -17,5 +19,6 @@ export default { }, nav_bar_height_ios: 44, nav_bar_height_android: 50, - backgroundColor: BACKGROUND_COLOR + backgroundColor: BACKGROUND_COLOR, + window_height: height, }; \ No newline at end of file diff --git a/js/util/AnalyticsUtil.js b/js/util/AnalyticsUtil.js new file mode 100644 index 0000000..0f2a9f5 --- /dev/null +++ b/js/util/AnalyticsUtil.js @@ -0,0 +1,5 @@ +/** + * Created by wangfei on 17/8/30. + */ +var { NativeModules } = require('react-native'); +module.exports = NativeModules.UMAnalyticsModule; \ No newline at end of file diff --git a/js/util/Utils.js b/js/util/Utils.js index 9fd41ac..a01af68 100644 --- a/js/util/Utils.js +++ b/js/util/Utils.js @@ -15,4 +15,16 @@ export default class Utils { } return false; } + + /** + * 检查key是否存在于keys中 + * @param keys + * @param key + */ + static checkKeyIsExist (keys, key) { + for (let i = 0, l = keys.length; i < l; i++) { + if (key.toLowerCase() === keys[i].name.toLowerCase()) return true + } + return false + } } \ No newline at end of file diff --git a/package.json b/package.json index 04d3bc0..b0a552a 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,16 @@ "react": "16.6.3", "react-native": "0.58.6", "react-native-check-box": "^2.1.7", + "react-native-code-push": "^5.6.0", "react-native-easy-toast": "^1.2.0", "react-native-event-bus": "^1.0.0", "react-native-gesture-handler": "^1.1.0", "react-native-htmlview": "^0.13.0", "react-native-parallax-scroll-view": "^0.21.3", "react-native-sortable-listview": "^0.2.8", + "react-native-splash-screen": "^3.2.0", "react-native-vector-icons": "^6.3.0", + "react-native-webview": "^5.6.2", "react-navigation": "^3.3.2", "react-navigation-redux-helpers": "^3.0.0", "react-redux": "^6.0.1", diff --git a/yarn.lock b/yarn.lock index 934d071..a836464 100644 --- a/yarn.lock +++ b/yarn.lock @@ -874,6 +874,11 @@ resolved "http://registry.npm.taobao.org/@types/node/download/@types/node-11.10.5.tgz#fbaca34086bdc118011e1f05c47688d432f2d571" integrity sha1-+6yjQIa9wRgBHh8FxHaI1DLy1XE= +"@types/node@^8.0.7": + version "8.10.45" + resolved "http://registry.npm.taobao.org/@types/node/download/@types/node-8.10.45.tgz#4c49ba34106bc7dced77ff6bae8eb6543cde8351" + integrity sha1-TEm6NBBrx9ztd/9rro62VDzeg1E= + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "http://registry.npm.taobao.org/@types/stack-utils/download/@types/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -935,6 +940,13 @@ acorn@^6.0.1: resolved "http://registry.npm.taobao.org/acorn/download/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha1-fSWuBbuK0fm2mRCOEJTs14hK3B8= +agent-base@4, agent-base@^4.1.0, agent-base@^4.2.0: + version "4.2.1" + resolved "http://registry.npm.taobao.org/agent-base/download/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha1-2J5ZmfeXh1Z0wH2H8mD8Qeg+jKk= + dependencies: + es6-promisify "^5.0.0" + ajv@^6.5.5: version "6.10.0" resolved "http://registry.npm.taobao.org/ajv/download/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" @@ -959,6 +971,11 @@ ansi-cyan@^0.1.1: dependencies: ansi-wrap "0.1.0" +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "http://registry.npm.taobao.org/ansi-escapes/download/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= + ansi-escapes@^3.0.0: version "3.2.0" resolved "http://registry.npm.taobao.org/ansi-escapes/download/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -1152,6 +1169,11 @@ assign-symbols@^1.0.0: resolved "http://registry.npm.taobao.org/assign-symbols/download/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types@0.x.x: + version "0.12.3" + resolved "http://registry.npm.taobao.org/ast-types/download/ast-types-0.12.3.tgz#2299c6201d34b2a749a2dd9f2de7ef5f0e84f423" + integrity sha1-IpnGIB00sqdJot2fLefvXw6E9CM= + astral-regex@^1.0.0: version "1.0.0" resolved "http://registry.npm.taobao.org/astral-regex/download/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1390,7 +1412,7 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-crc32@^0.2.13: +buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" resolved "http://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= @@ -1466,7 +1488,7 @@ caseless@~0.12.0: resolved "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.1.1: +chalk@^1.0.0, chalk@^1.1.1: version "1.1.3" resolved "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -1511,6 +1533,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-cursor@^1.0.1: + version "1.0.2" + resolved "http://registry.npm.taobao.org/cli-cursor/download/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + cli-cursor@^2.1.0: version "2.1.0" resolved "http://registry.npm.taobao.org/cli-cursor/download/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -1551,6 +1580,18 @@ code-point-at@^1.0.0: resolved "http://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +code-push@2.0.6: + version "2.0.6" + resolved "http://registry.npm.taobao.org/code-push/download/code-push-2.0.6.tgz#fb4e140c90fdb7fa047f3be6f53caacc8915d0b8" + integrity sha1-+04UDJD9t/oEfzvm9TyqzIkV0Lg= + dependencies: + q "^1.4.1" + recursive-fs "0.1.4" + slash "1.0.0" + superagent "^3.8.0" + superagent-proxy "^1.0.3" + yazl "^2.4.1" + collection-visit@^1.0.0: version "1.0.0" resolved "http://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1608,7 +1649,7 @@ compare-versions@^3.2.1: resolved "http://registry.npm.taobao.org/compare-versions/download/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26" integrity sha1-4HR99cnLfwVNbT3D4dvERPnpKyY= -component-emitter@^1.2.1: +component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.2.1" resolved "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= @@ -1638,7 +1679,7 @@ concat-map@0.0.1: resolved "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0: +concat-stream@^1.4.7, concat-stream@^1.6.0: version "1.6.2" resolved "http://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ= @@ -1670,6 +1711,11 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0: dependencies: safe-buffer "~5.1.1" +cookiejar@^2.1.0: + version "2.1.2" + resolved "http://registry.npm.taobao.org/cookiejar/download/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha1-3YojVTB1L5iPmghE8/xYnjERElw= + copy-descriptor@^0.1.0: version "0.1.1" resolved "http://registry.npm.taobao.org/copy-descriptor/download/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1757,6 +1803,13 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@2: + version "2.0.1" + resolved "http://registry.npm.taobao.org/data-uri-to-buffer/download/data-uri-to-buffer-2.0.1.tgz#ca8f56fe38b1fd329473e9d1b4a9afcd8ce1c045" + integrity sha1-yo9W/jix/TKUc+nRtKmvzYzhwEU= + dependencies: + "@types/node" "^8.0.7" + data-urls@^1.0.0: version "1.1.0" resolved "http://registry.npm.taobao.org/data-urls/download/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" @@ -1773,20 +1826,27 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^3.2.6: - version "3.2.6" - resolved "http://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha1-6D0X3hbYp++3cX7b5fsQE17uYps= +debug@3.1.0: + version "3.1.0" + resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE= dependencies: - ms "^2.1.1" + ms "2.0.0" -debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "http://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E= dependencies: ms "^2.1.1" +debug@^3.1.0, debug@^3.2.6: + version "3.2.6" + resolved "http://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha1-6D0X3hbYp++3cX7b5fsQE17uYps= + dependencies: + ms "^2.1.1" + decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1843,6 +1903,15 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +degenerator@^1.0.4: + version "1.0.4" + resolved "http://registry.npm.taobao.org/degenerator/download/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + delayed-stream@~1.0.0: version "1.0.0" resolved "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2001,17 +2070,29 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-promise@^4.0.3: + version "4.2.6" + resolved "http://registry.npm.taobao.org/es6-promise/download/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" + integrity sha1-toXt2CWIhjZepitX0w3ij63Nl08= + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "http://registry.npm.taobao.org/es6-promisify/download/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + escape-html@~1.0.3: version "1.0.3" resolved "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.9.1: +escodegen@1.x.x, escodegen@^1.9.1: version "1.11.1" resolved "http://registry.npm.taobao.org/escodegen/download/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" integrity sha1-xIX/jWtM24nif0qFbpHxGEAcpRA= @@ -2023,7 +2104,7 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -esprima@^3.1.3: +esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "http://registry.npm.taobao.org/esprima/download/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= @@ -2101,6 +2182,11 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +exit-hook@^1.0.0: + version "1.1.1" + resolved "http://registry.npm.taobao.org/exit-hook/download/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + exit@^0.1.2: version "0.1.2" resolved "http://registry.npm.taobao.org/exit/download/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2167,11 +2253,20 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo= +external-editor@^1.0.1: + version "1.1.1" + resolved "http://registry.npm.taobao.org/external-editor/download/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b" + integrity sha1-Etew24UPf/fnCBuvQAVwAGDEYAs= + dependencies: + extend "^3.0.0" + spawn-sync "^1.0.15" + tmp "^0.0.29" + external-editor@^2.0.4: version "2.2.0" resolved "http://registry.npm.taobao.org/external-editor/download/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" @@ -2292,6 +2387,14 @@ fbjs@^1.0.0: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +figures@^1.3.5: + version "1.7.0" + resolved "http://registry.npm.taobao.org/figures/download/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + figures@^2.0.0: version "2.0.0" resolved "http://registry.npm.taobao.org/figures/download/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -2299,6 +2402,11 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +file-uri-to-path@1: + version "1.0.0" + resolved "http://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90= + filename-regex@^2.0.0: version "2.0.1" resolved "http://registry.npm.taobao.org/filename-regex/download/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -2393,7 +2501,7 @@ forever-agent@~0.6.1: resolved "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.3.2: +form-data@^2.3.1, form-data@~2.3.2: version "2.3.3" resolved "http://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha1-3M5SwF9kTymManq5Nr1yTO/786Y= @@ -2402,6 +2510,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formidable@^1.2.0: + version "1.2.1" + resolved "http://registry.npm.taobao.org/formidable/download/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" + integrity sha1-cPt8oCkO5v+WEJBBX0s989IIJlk= + fragment-cache@^0.2.1: version "0.2.1" resolved "http://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -2443,6 +2556,14 @@ fsevents@^1.2.3: nan "^2.9.2" node-pre-gyp "^0.10.0" +ftp@~0.3.10: + version "0.3.10" + resolved "http://registry.npm.taobao.org/ftp/download/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2490,6 +2611,18 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-uri@^2.0.0: + version "2.0.3" + resolved "http://registry.npm.taobao.org/get-uri/download/get-uri-2.0.3.tgz#fa13352269781d75162c6fc813c9e905323fbab5" + integrity sha1-+hM1Iml4HXUWLG/IE8npBTI/urU= + dependencies: + data-uri-to-buffer "2" + debug "4" + extend "~3.0.2" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "3" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "http://registry.npm.taobao.org/get-value/download/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2517,6 +2650,17 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" +glob@^5.0.15: + version "5.0.15" + resolved "http://registry.npm.taobao.org/glob/download/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: version "7.1.3" resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -2683,7 +2827,7 @@ htmlparser2-without-node-native@^3.9.2: inherits "^2.0.1" readable-stream "^2.0.2" -http-errors@~1.6.2: +http-errors@1.6.3, http-errors@~1.6.2: version "1.6.3" resolved "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= @@ -2693,6 +2837,14 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "http://registry.npm.taobao.org/http-proxy-agent/download/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha1-5IIb7vWyFCogJr1zkm/lN2McVAU= + dependencies: + agent-base "4" + debug "3.1.0" + http-signature@~1.2.0: version "1.2.0" resolved "http://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2702,6 +2854,21 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +https-proxy-agent@^2.2.1: + version "2.2.1" + resolved "http://registry.npm.taobao.org/https-proxy-agent/download/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" + integrity sha1-UVUpcPoE1yPgTFbQQXjD+SWSu8A= + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" + +iconv-lite@0.4.23: + version "0.4.23" + resolved "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM= + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2750,7 +2917,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -2760,6 +2927,26 @@ ini@~1.3.0: resolved "http://registry.npm.taobao.org/ini/download/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc= +inquirer@1.1.2: + version "1.1.2" + resolved "http://registry.npm.taobao.org/inquirer/download/inquirer-1.1.2.tgz#ac3ba5f06b8e7291abd9f22912c03f09cfe2dd1f" + integrity sha1-rDul8GuOcpGr2fIpEsA/Cc/i3R8= + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + external-editor "^1.0.1" + figures "^1.3.5" + lodash "^4.3.0" + mute-stream "0.0.6" + pinkie-promise "^2.0.0" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + inquirer@^3.0.6: version "3.3.0" resolved "http://registry.npm.taobao.org/inquirer/download/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" @@ -2780,7 +2967,7 @@ inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -invariant@^2.2.2, invariant@^2.2.4: +invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "http://registry.npm.taobao.org/invariant/download/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY= @@ -2797,6 +2984,11 @@ invert-kv@^2.0.0: resolved "http://registry.npm.taobao.org/invert-kv/download/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha1-c5P1r6Weyf9fZ6J2INEcIm4+7AI= +ip@^1.1.4, ip@^1.1.5: + version "1.1.5" + resolved "http://registry.npm.taobao.org/ip/download/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -3750,7 +3942,7 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4 dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-cache@^4.0.1: +lru-cache@^4.0.1, lru-cache@^4.1.2: version "4.1.5" resolved "http://registry.npm.taobao.org/lru-cache/download/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80= @@ -3824,6 +4016,11 @@ merge@^1.2.0: resolved "http://registry.npm.taobao.org/merge/download/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha1-OL6/gMMiCopIe2/Ps5QbsRcgwUU= +methods@^1.1.1: + version "1.1.2" + resolved "http://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + metro-babel-register@^0.49.1: version "0.49.2" resolved "http://registry.npm.taobao.org/metro-babel-register/download/metro-babel-register-0.49.2.tgz#746c73311135bd6c2af4d83c2cc6c5cbcf0e8a65" @@ -4118,7 +4315,7 @@ mime@1.4.1: resolved "http://registry.npm.taobao.org/mime/download/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY= -mime@^1.3.4: +mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "http://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE= @@ -4135,7 +4332,7 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM= @@ -4208,6 +4405,11 @@ ms@^2.1.1: resolved "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo= +mute-stream@0.0.6: + version "0.0.6" + resolved "http://registry.npm.taobao.org/mute-stream/download/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" + integrity sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s= + mute-stream@0.0.7: version "0.0.7" resolved "http://registry.npm.taobao.org/mute-stream/download/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -4254,6 +4456,11 @@ negotiator@0.6.1: resolved "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= +netmask@^1.0.6: + version "1.0.6" + resolved "http://registry.npm.taobao.org/netmask/download/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + nice-try@^1.0.4: version "1.0.5" resolved "http://registry.npm.taobao.org/nice-try/download/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -4461,6 +4668,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "http://registry.npm.taobao.org/onetime/download/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + onetime@^2.0.0: version "2.0.1" resolved "http://registry.npm.taobao.org/onetime/download/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -4523,7 +4735,12 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-shim@^0.1.2: + version "0.1.3" + resolved "http://registry.npm.taobao.org/os-shim/download/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + integrity sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc= + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -4601,6 +4818,31 @@ p-try@^2.0.0: resolved "http://registry.npm.taobao.org/p-try/download/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" integrity sha1-hQgLuHxkaI+keZb+j3376CEXYLE= +pac-proxy-agent@^2.0.1: + version "2.0.2" + resolved "http://registry.npm.taobao.org/pac-proxy-agent/download/pac-proxy-agent-2.0.2.tgz#90d9f6730ab0f4d2607dcdcd4d3d641aa26c3896" + integrity sha1-kNn2cwqw9NJgfc3NTT1kGqJsOJY= + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + get-uri "^2.0.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + pac-resolver "^3.0.0" + raw-body "^2.2.0" + socks-proxy-agent "^3.0.0" + +pac-resolver@^3.0.0: + version "3.0.0" + resolved "http://registry.npm.taobao.org/pac-resolver/download/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" + integrity sha1-auoweH2wqJFwTet4AKcip2FabyY= + dependencies: + co "^4.6.0" + degenerator "^1.0.4" + ip "^1.1.5" + netmask "^1.0.6" + thunkify "^2.1.2" + parse-glob@^3.0.4: version "3.0.4" resolved "http://registry.npm.taobao.org/parse-glob/download/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -4687,6 +4929,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +pegjs@^0.10.0: + version "0.10.0" + resolved "http://registry.npm.taobao.org/pegjs/download/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" + integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0= + performance-now@^2.1.0: version "2.1.0" resolved "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -4702,6 +4949,18 @@ pify@^3.0.0: resolved "http://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + pirates@^4.0.0, pirates@^4.0.1: version "4.0.1" resolved "http://registry.npm.taobao.org/pirates/download/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" @@ -4732,7 +4991,7 @@ plist@2.0.1: xmlbuilder "8.2.2" xmldom "0.1.x" -plist@^3.0.0: +plist@3.0.1, plist@^3.0.0: version "3.0.1" resolved "http://registry.npm.taobao.org/plist/download/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" integrity sha1-qbkx0XwwTokS7wujvdYYK68uH4w= @@ -4828,6 +5087,25 @@ prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, object-assign "^4.1.1" react-is "^16.8.1" +proxy-agent@2: + version "2.3.1" + resolved "http://registry.npm.taobao.org/proxy-agent/download/proxy-agent-2.3.1.tgz#3d49d863d46cf5f37ca8394848346ea02373eac6" + integrity sha1-PUnYY9Rs9fN8qDlISDRuoCNz6sY= + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + lru-cache "^4.1.2" + pac-proxy-agent "^2.0.1" + proxy-from-env "^1.0.0" + socks-proxy-agent "^3.0.0" + +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "http://registry.npm.taobao.org/proxy-from-env/download/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + pseudomap@^1.0.2: version "1.0.2" resolved "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -4856,6 +5134,16 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "http://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha1-tYsBCsQMIsVldhbI0sLALHv0eew= +q@^1.4.1: + version "1.5.1" + resolved "http://registry.npm.taobao.org/q/download/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@^6.5.1: + version "6.7.0" + resolved "http://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha1-QdwaAV49WB8WIXdr4xr7KHapsbw= + qs@~6.5.2: version "6.5.2" resolved "http://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -4883,6 +5171,16 @@ range-parser@~1.2.0: resolved "http://registry.npm.taobao.org/range-parser/download/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= +raw-body@^2.2.0: + version "2.3.3" + resolved "http://registry.npm.taobao.org/raw-body/download/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha1-GzJOzmtXBuFThVvBFIxlu39uoMM= + dependencies: + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" + unpipe "1.0.0" + rc@^1.2.7: version "1.2.8" resolved "http://registry.npm.taobao.org/rc/download/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -4928,6 +5226,19 @@ react-native-check-box@^2.1.7: dependencies: prop-types "^15.5.7" +react-native-code-push@^5.6.0: + version "5.6.0" + resolved "http://registry.npm.taobao.org/react-native-code-push/download/react-native-code-push-5.6.0.tgz#e905d32d188c5f7bcff446d813271d03a69543fd" + integrity sha1-6QXTLRiMX3vP9EbYEycdA6aVQ/0= + dependencies: + code-push "2.0.6" + glob "^5.0.15" + hoist-non-react-statics "^2.3.1" + inquirer "1.1.2" + plist "3.0.1" + semver "^5.6.0" + xcode "1.0.0" + react-native-easy-toast@^1.2.0: version "1.2.0" resolved "http://registry.npm.taobao.org/react-native-easy-toast/download/react-native-easy-toast-1.2.0.tgz#0f70bcb99e3306cda4800c244bfb4a67d42276ed" @@ -4992,6 +5303,11 @@ react-native-sortable-listview@^0.2.8: resolved "http://registry.npm.taobao.org/react-native-sortable-listview/download/react-native-sortable-listview-0.2.8.tgz#0d514fa73eb406a46baea96aa38a71e40257af90" integrity sha1-DVFPpz60BqRrrqlqo4px5AJXr5A= +react-native-splash-screen@^3.2.0: + version "3.2.0" + resolved "http://registry.npm.taobao.org/react-native-splash-screen/download/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45" + integrity sha1-1H7IVXsbqYjuPqmNAUYwgbYP/0U= + react-native-tab-view@^1.0.0, react-native-tab-view@^1.2.0: version "1.3.2" resolved "http://registry.npm.taobao.org/react-native-tab-view/download/react-native-tab-view-1.3.2.tgz#c4e43a538dcacce151938745cea09176beeccbc3" @@ -5008,6 +5324,14 @@ react-native-vector-icons@^6.3.0: prop-types "^15.6.2" yargs "^8.0.2" +react-native-webview@^5.6.2: + version "5.6.2" + resolved "http://registry.npm.taobao.org/react-native-webview/download/react-native-webview-5.6.2.tgz#aa257f01d0038c514874d1c41d99528f83274d8c" + integrity sha1-qiV/AdADjFFIdNHEHZlSj4MnTYw= + dependencies: + escape-string-regexp "1.0.5" + invariant "2.2.4" + react-native@0.58.6: version "0.58.6" resolved "http://registry.npm.taobao.org/react-native/download/react-native-0.58.6.tgz#a6d273cd2f51b0e3b141dad5f19622e0000497b3" @@ -5189,7 +5513,26 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: +readable-stream@1.1.x: + version "1.1.14" + resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@3: + version "3.3.0" + resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" + integrity sha1-y4ARqtAC63F78EApH+uoVpyYb7k= + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.6" resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8= @@ -5209,6 +5552,11 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +recursive-fs@0.1.4: + version "0.1.4" + resolved "http://registry.npm.taobao.org/recursive-fs/download/recursive-fs-0.1.4.tgz#47e08b1ddab8d7d9a960aa0d0daea76f875b63fa" + integrity sha1-R+CLHdq419mpYKoNDa6nb4dbY/o= + redux-thunk@^2.3.0: version "2.3.0" resolved "http://registry.npm.taobao.org/redux-thunk/download/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" @@ -5386,6 +5734,14 @@ resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: dependencies: path-parse "^1.0.6" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "http://registry.npm.taobao.org/restore-cursor/download/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "http://registry.npm.taobao.org/restore-cursor/download/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -5435,6 +5791,11 @@ rx-lite@*, rx-lite@^4.0.8: resolved "http://registry.npm.taobao.org/rx-lite/download/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= +rx@^4.1.0: + version "4.1.0" + resolved "http://registry.npm.taobao.org/rx/download/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= + safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -5507,6 +5868,11 @@ scheduler@^0.11.2: resolved "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha1-fnQlb7qknHWqfHogXMInmcrIAAQ= +semver@^5.6.0: + version "5.7.0" + resolved "http://registry.npm.taobao.org/semver/download/semver-5.7.0.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha1-eQp89v6lRZuslhELKbYEEtyP+Ws= + send@0.16.2: version "0.16.2" resolved "http://registry.npm.taobao.org/send/download/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" @@ -5622,6 +5988,11 @@ sisteransi@^1.0.0: resolved "http://registry.npm.taobao.org/sisteransi/download/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" integrity sha1-d9liL/kJCA8cGeX0od8MGwonuIw= +slash@1.0.0: + version "1.0.0" + resolved "http://registry.npm.taobao.org/slash/download/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + slash@^2.0.0: version "2.0.0" resolved "http://registry.npm.taobao.org/slash/download/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -5632,6 +6003,11 @@ slide@^1.1.5: resolved "http://registry.npm.taobao.org/slide/download/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +smart-buffer@^1.0.13: + version "1.1.15" + resolved "http://registry.npm.taobao.org/smart-buffer/download/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + integrity sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY= + snapdragon-node@^2.0.1: version "2.1.1" resolved "http://registry.npm.taobao.org/snapdragon-node/download/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -5662,6 +6038,22 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@^3.0.0: + version "3.0.1" + resolved "http://registry.npm.taobao.org/socks-proxy-agent/download/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659" + integrity sha1-Lq58+OKoLTRWV2FTmn+XGMVhdlk= + dependencies: + agent-base "^4.1.0" + socks "^1.1.10" + +socks@^1.1.10: + version "1.1.10" + resolved "http://registry.npm.taobao.org/socks/download/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" + integrity sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o= + dependencies: + ip "^1.1.4" + smart-buffer "^1.0.13" + source-map-resolve@^0.5.0: version "0.5.2" resolved "http://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -5696,6 +6088,14 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha1-dHIq8y6WFOnCh6jQu95IteLxomM= +spawn-sync@^1.0.15: + version "1.0.15" + resolved "http://registry.npm.taobao.org/spawn-sync/download/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + integrity sha1-sAeZVX63+wyDdsKdROih6mfldHY= + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + spdx-correct@^3.0.0: version "3.1.0" resolved "http://registry.npm.taobao.org/spdx-correct/download/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -5822,6 +6222,18 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string_decoder@^1.1.1: + version "1.2.0" + resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha1-/obnOLGVRK/nBGkkOyoe6SQOro0= + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -5865,6 +6277,30 @@ strip-json-comments@~2.0.1: resolved "http://registry.npm.taobao.org/strip-json-comments/download/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +superagent-proxy@^1.0.3: + version "1.0.3" + resolved "http://registry.npm.taobao.org/superagent-proxy/download/superagent-proxy-1.0.3.tgz#acfa776672f11c24a90ad575e855def8be44f741" + integrity sha1-rPp3ZnLxHCSpCtV16FXe+L5E90E= + dependencies: + debug "^3.1.0" + proxy-agent "2" + +superagent@^3.8.0: + version "3.8.3" + resolved "http://registry.npm.taobao.org/superagent/download/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" + integrity sha1-Rg6g29t9WxG8T3jeulZfhqF44Sg= + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.2.0" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.3.5" + supports-color@^2.0.0: version "2.0.0" resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -5943,11 +6379,23 @@ through@^2.3.6: resolved "http://registry.npm.taobao.org/through/download/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunkify@^2.1.2: + version "2.1.2" + resolved "http://registry.npm.taobao.org/thunkify/download/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= + time-stamp@^1.0.0: version "1.1.0" resolved "http://registry.npm.taobao.org/time-stamp/download/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= +tmp@^0.0.29: + version "0.0.29" + resolved "http://registry.npm.taobao.org/tmp/download/tmp-0.0.29.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Ftmp%2Fdownload%2Ftmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" + integrity sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA= + dependencies: + os-tmpdir "~1.0.1" + tmp@^0.0.33: version "0.0.33" resolved "http://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -6106,7 +6554,7 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" -unpipe@~1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= @@ -6136,7 +6584,7 @@ use@^3.1.0: resolved "http://registry.npm.taobao.org/use/download/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8= -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -6154,6 +6602,11 @@ utils-merge@1.0.1: resolved "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@3.0.1: + version "3.0.1" + resolved "http://registry.npm.taobao.org/uuid/download/uuid-3.0.1.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + integrity sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE= + uuid@^3.3.2: version "3.3.2" resolved "http://registry.npm.taobao.org/uuid/download/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -6327,6 +6780,15 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" +xcode@1.0.0: + version "1.0.0" + resolved "http://registry.npm.taobao.org/xcode/download/xcode-1.0.0.tgz#e1f5b1443245ded38c180796df1a10fdeda084ec" + integrity sha1-4fWxRDJF3tOMGAeW3xoQ/e2ghOw= + dependencies: + pegjs "^0.10.0" + simple-plist "^0.2.1" + uuid "3.0.1" + xcode@^1.0.0: version "1.1.0" resolved "http://registry.npm.taobao.org/xcode/download/xcode-1.1.0.tgz#9fcb63f417a9af377bfb743a5c22afce4e1da964" @@ -6367,6 +6829,11 @@ xpipe@^1.0.5: resolved "http://registry.npm.taobao.org/xpipe/download/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf" integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98= +xregexp@2.0.0: + version "2.0.0" + resolved "http://registry.npm.taobao.org/xregexp/download/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + xtend@~4.0.1: version "4.0.1" resolved "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -6462,3 +6929,10 @@ yargs@^9.0.0: which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^7.0.0" + +yazl@^2.4.1: + version "2.5.1" + resolved "http://registry.npm.taobao.org/yazl/download/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" + integrity sha1-o9ZdPdZZpbCTeFDoYJ8i//orXDU= + dependencies: + buffer-crc32 "~0.2.3" From 79b017599f018d723559ddb318333774d7fbef6c Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 9 Apr 2019 17:28:48 +0800 Subject: [PATCH 10/20] readme --- README.md | 28 ++++++++++++++++++++++++++-- assets/qrcode.png | Bin 0 -> 8920 bytes 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 assets/qrcode.png diff --git a/README.md b/README.md index 66c9b05..fd201c9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ -# Github_RN -nothing +# GitHub Popular + +这是一个用来查看GitHub最受欢迎与最热项目的App,它基于React Native支持Android和iOS双平台。 + +## 目录 + +*[下载] (#下载) + +## 下载 +![APP QRcode](https://github.com/songdongdong123/Github_RN/assets/qrcode.png) + +## 功能与特性 + +* 支持订阅多种编程语言; +* 支持添加/删除编程语言; +* 支持自定义语言/标签,排序; +* 支持收藏喜欢的项目; +* 支持多种颜色主题自由切换; +* 支持搜索,并自持自定义订阅关键字; + +## 运行调试 + +1. 准备React Native环境,可参考: [getting-started](https://reactnative.cn/docs/0.51/getting-started.html)。 +2. Clone [GitHub Popular](https://github.com/songdongdong123/Github_RN.git),然后终端进入项目根目录。 +3. 终端运行 `npm install`||`yarn install`。 +4. 然后运行 `react-native run-android` || `react-native run-ios`。 \ No newline at end of file diff --git a/assets/qrcode.png b/assets/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..145ba13c67c989a7b35d5c63fc5dfc26c31a349f GIT binary patch literal 8920 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 literal 0 HcmV?d00001 From 7da37f88297f4cf964de46847edadde1acf0639b Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 9 Apr 2019 17:30:38 +0800 Subject: [PATCH 11/20] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd201c9..263daad 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ *[下载] (#下载) ## 下载 -![APP QRcode](https://github.com/songdongdong123/Github_RN/assets/qrcode.png) +![APP QRcode](https://github.com/songdongdong123/Github_RN/blob/master/assets/qrcode.png) ## 功能与特性 From 8821793c6af43ef6e72ad48705ec1757b5d2e507 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 9 Apr 2019 17:32:33 +0800 Subject: [PATCH 12/20] readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 263daad..62c1518 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ## 目录 -*[下载] (#下载) +* [下载] (#下载) +* [功能] (#功能) +* [运行调试] (#运行调试) ## 下载 ![APP QRcode](https://github.com/songdongdong123/Github_RN/blob/master/assets/qrcode.png) From abd27898c934957d8ada416c587ac6ff50eb168d Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Tue, 9 Apr 2019 17:35:02 +0800 Subject: [PATCH 13/20] modify readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 62c1518..0f59651 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ## 目录 -* [下载] (#下载) -* [功能] (#功能) -* [运行调试] (#运行调试) +* [下载](#下载) +* [功能](#功能) +* [运行调试](#运行调试) ## 下载 ![APP QRcode](https://github.com/songdongdong123/Github_RN/blob/master/assets/qrcode.png) From c90491dcc4591e96d34997611ece8d697418a247 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Wed, 10 Apr 2019 14:23:25 +0800 Subject: [PATCH 14/20] codepush --- README.md | 2 +- android/app/build.gradle | 6 + .../java/com/github_rn/MainApplication.java | 8 + android/settings.gradle | 2 + js/common/MORE_MENU.js | 1 + js/navigator/AppNavigator.js | 7 + js/page/CodePushPage.js | 182 ++++++++++++++++++ js/page/Mypage.js | 6 + js/redux/reducer/theme/index.js | 1 - 9 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 js/page/CodePushPage.js diff --git a/README.md b/README.md index 0f59651..8c76c48 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## 下载 ![APP QRcode](https://github.com/songdongdong123/Github_RN/blob/master/assets/qrcode.png) -## 功能与特性 +## 功能 * 支持订阅多种编程语言; * 支持添加/删除编程语言; diff --git a/android/app/build.gradle b/android/app/build.gradle index 3dbb8ec..c1c8ed6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -77,6 +77,7 @@ project.ext.react = [ ] apply from: "../../node_modules/react-native/react.gradle" +apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" /** * Set this to true to create two separate APKs instead of one: @@ -121,10 +122,14 @@ android { } } buildTypes { + releaseStaging { + buildConfigField "String", "CODEPUSH_KEY", '""' + } release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" signingConfig signingConfigs.release + buildConfigField "String", "CODEPUSH_KEY", '""' } } lintOptions { @@ -146,6 +151,7 @@ android { } dependencies { + implementation project(':react-native-code-push') implementation project(':react-native-webview') implementation project(':react-native-splash-screen') implementation project(':react-native-vector-icons') diff --git a/android/app/src/main/java/com/github_rn/MainApplication.java b/android/app/src/main/java/com/github_rn/MainApplication.java index 6380c42..cf034fc 100644 --- a/android/app/src/main/java/com/github_rn/MainApplication.java +++ b/android/app/src/main/java/com/github_rn/MainApplication.java @@ -3,6 +3,7 @@ import android.app.Application; import com.facebook.react.ReactApplication; +import com.microsoft.codepush.react.CodePush; import com.reactnativecommunity.webview.RNCWebViewPackage; import org.devio.rn.splashscreen.SplashScreenReactPackage; import com.oblador.vectoricons.VectorIconsPackage; @@ -19,6 +20,12 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + + @Override + protected String getJSBundleFile() { + return CodePush.getJSBundleFile(); + } + @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; @@ -28,6 +35,7 @@ public boolean getUseDeveloperSupport() { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new CodePush(getResources().getString(R.string.reactNativeCodePush_androidDeploymentKey), getApplicationContext(), BuildConfig.DEBUG), new RNCWebViewPackage(), new SplashScreenReactPackage(), new VectorIconsPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index bfc4855..e7ed515 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'Github_RN' +include ':react-native-code-push' +project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') include ':react-native-webview' project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') include ':react-native-splash-screen' diff --git a/js/common/MORE_MENU.js b/js/common/MORE_MENU.js index 2870cbf..d02ac5e 100644 --- a/js/common/MORE_MENU.js +++ b/js/common/MORE_MENU.js @@ -20,4 +20,5 @@ export const MORE_MENU = { Tutorial: {name:'教程', Icons:Ionicons, icon: 'ios-bookmarks'}, Feedback: {name:'反馈', Icons:MaterialIcons, icon: 'feedback'}, Share: {name:'分享', Icons:Ionicons, icon: 'md-share'}, + CodePush: { name: 'CodePush', Icons: Ionicons, icon: 'ios-planet' }, }; diff --git a/js/navigator/AppNavigator.js b/js/navigator/AppNavigator.js index 4b0cb5e..62ef198 100644 --- a/js/navigator/AppNavigator.js +++ b/js/navigator/AppNavigator.js @@ -24,6 +24,7 @@ import AboutMePage from '../page/about/AboutMePage'; import CustomKeyPage from '../page/CustomKeyPage'; import SortKeyPage from '../page/SortKeyPage'; import SearchPage from '../page/SearchPage'; +import CodePushPage from '../page/CodePushPage' export const rootCom = 'Init'; //设置跟路由 @@ -88,6 +89,12 @@ const MainNavigator = createStackNavigator({ header: null } }, + CodePushPage : { + screen: CodePushPage , + navigationOptions: { + header: null + } + }, AxiosDemoPage: { screen: AxiosDemoPage, navigationOptions: { diff --git a/js/page/CodePushPage.js b/js/page/CodePushPage.js new file mode 100644 index 0000000..1b44dab --- /dev/null +++ b/js/page/CodePushPage.js @@ -0,0 +1,182 @@ +import React, { Component } from 'react' +import { Dimensions, StyleSheet, Text, TouchableOpacity, } from 'react-native' +import CodePush from 'react-native-code-push' +import ViewUtils from '../util/ViewUtil' +import NavigationBar from '../common/NavigationBar' +import SafeAreaViewPlus from '../common/SafeAreaViewPlus' +import BackPressComponent from '../common/BackPressComponent' +import GlobalStyles from '../res/styles/GobalStyles' +import NavigationUtil from '../navigator/NavigationUtil' + +class CodePushPage extends Component { + constructor (props) { + super(props) + this.state = { restartAllowed: true } + this.backPress = new BackPressComponent({ backPress: this.onBackPress }) + } + + componentDidMount () { + this.backPress.componentDidMount() + } + + componentWillUnmount () { + this.backPress.componentWillUnmount() + } + + onBackPress = () => { + NavigationUtil.goBack({navigation:this.props.navigation}); + return true + } + + codePushStatusDidChange (syncStatus) { + switch (syncStatus) { + case CodePush.SyncStatus.CHECKING_FOR_UPDATE: + this.setState({ syncMessage: 'Checking for update.' }) + break + case CodePush.SyncStatus.DOWNLOADING_PACKAGE: + this.setState({ syncMessage: 'Downloading package.' }) + break + case CodePush.SyncStatus.AWAITING_USER_ACTION: + this.setState({ syncMessage: 'Awaiting user action.' }) + break + case CodePush.SyncStatus.INSTALLING_UPDATE: + this.setState({ syncMessage: 'Installing update.' }) + break + case CodePush.SyncStatus.UP_TO_DATE: + this.setState({ syncMessage: 'App up to date.', progress: false }) + break + case CodePush.SyncStatus.UPDATE_IGNORED: + this.setState({ syncMessage: 'Update cancelled by user.', progress: false }) + break + case CodePush.SyncStatus.UPDATE_INSTALLED: + this.setState({ syncMessage: 'Update installed and will be applied on restart.', progress: false }) + break + case CodePush.SyncStatus.UNKNOWN_ERROR: + this.setState({ syncMessage: 'An unknown error occurred.', progress: false }) + break + } + } + + codePushDownloadDidProgress (progress) { + this.setState({ progress }) + } + + toggleAllowRestart () { + this.state.restartAllowed + ? CodePush.disallowRestart() + : CodePush.allowRestart() + + this.setState({ restartAllowed: !this.state.restartAllowed }) + } + + getUpdateMetadata () { + CodePush.getUpdateMetadata(CodePush.UpdateState.RUNNING) + .then((metadata: LocalPackage) => { + this.setState({ + syncMessage: metadata ? JSON.stringify(metadata) : 'Running binary version', + progress: false + }) + }, (error: any) => { + this.setState({ syncMessage: 'Error: ' + error, progress: false }) + }) + } + + /** Update is downloaded silently, and applied on restart (recommended) */ + sync () { + CodePush.sync( + {}, + this.codePushStatusDidChange.bind(this), + this.codePushDownloadDidProgress.bind(this) + ) + } + + /** Update pops a confirmation dialog, and then immediately reboots the app */ + syncImmediate () { + CodePush.sync( + { installMode: CodePush.InstallMode.IMMEDIATE, updateDialog: true }, + this.codePushStatusDidChange.bind(this), + this.codePushDownloadDidProgress.bind(this) + ) + } + + render () { + let progressView + if (this.state.progress) { + progressView = ( + {this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes + received + ) + } + const { theme } = this.props.navigation.state.params + return ( + + NavigationUtil.goBack({navigation: this.props.navigation}))} + title={'CodePush'} + /> + + Welcome to CodePush! + + + Press for background sync + + + Press for dialog-driven sync + + {progressView} + + Restart {this.state.restartAllowed ? 'allowed' : 'forbidden'} + + + Press for Update Metadata + + {this.state.syncMessage || ''} + + ) + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + image: { + margin: 30, + width: Dimensions.get('window').width - 100, + height: 365 * (Dimensions.get('window').width - 100) / 651, + }, + messages: { + marginTop: 30, + textAlign: 'center', + }, + restartToggleButton: { + color: 'blue', + fontSize: 17 + }, + syncButton: { + color: 'green', + fontSize: 17 + }, + welcome: { + fontSize: 20, + textAlign: 'center', + margin: 20 + }, +}) + +/** + * Configured with a MANUAL check frequency for easy testing. For production apps, it is recommended to configure a + * different check frequency, such as ON_APP_START, for a 'hands-off' approach where CodePush.sync() does not + * need to be explicitly called. All options of CodePush.sync() are also available in this decorator. + */ +let codePushOptions = { checkFrequency: CodePush.CheckFrequency.MANUAL } + +CodePushPage = CodePush(codePushOptions)(CodePushPage) + +export default CodePushPage \ No newline at end of file diff --git a/js/page/Mypage.js b/js/page/Mypage.js index f016eb3..efe8a66 100644 --- a/js/page/Mypage.js +++ b/js/page/Mypage.js @@ -42,6 +42,9 @@ export default class My extends Component { RouteName = 'SortKeyPage'; params.flag = FLAG_LANGUAGE.flag_language; break; + case MORE_MENU.CodePush: + RouteName = 'CodePushPage'; + break; case MORE_MENU.Custom_Theme: const {onShowCustomThemeView} = this.props; onShowCustomThemeView(true); @@ -137,6 +140,9 @@ export default class My extends Component { {/* 反馈 */} {this.getItem(MORE_MENU.Feedback)} + {/* codepush */} + + {this.getItem(MORE_MENU.CodePush)} ); diff --git a/js/redux/reducer/theme/index.js b/js/redux/reducer/theme/index.js index eb7ec12..d66a3e6 100644 --- a/js/redux/reducer/theme/index.js +++ b/js/redux/reducer/theme/index.js @@ -1,6 +1,5 @@ import Types from '../../action/action_types'; import ThemeFactory, {ThemeFlags} from '../../../res/styles/ThemeFactory'; -console.log(ThemeFactory.createTheme(ThemeFlags.Default)) const defaultState = { theme: ThemeFactory.createTheme(ThemeFlags.Default), customThemeViewVisible: false From 772a6437a4eeb7af595ee09eaaae23c5b04b42b0 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Wed, 10 Apr 2019 15:23:47 +0800 Subject: [PATCH 15/20] =?UTF-8?q?=E9=9B=86=E6=88=90umeng=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/github_rn/MainApplication.java | 4 +- .../invokenative/AnalyticsModule.java | 199 ++++++++++++++++++ .../invokenative/DplusReactPackage.java | 39 ++++ .../github_rn/invokenative/RNUMConfigure.java | 32 +++ js/page/PopularPage.js | 2 + js/page/WelcomePage.js | 3 +- 6 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/java/com/github_rn/invokenative/AnalyticsModule.java create mode 100644 android/app/src/main/java/com/github_rn/invokenative/DplusReactPackage.java create mode 100644 android/app/src/main/java/com/github_rn/invokenative/RNUMConfigure.java diff --git a/android/app/src/main/java/com/github_rn/MainApplication.java b/android/app/src/main/java/com/github_rn/MainApplication.java index cf034fc..eef1fd8 100644 --- a/android/app/src/main/java/com/github_rn/MainApplication.java +++ b/android/app/src/main/java/com/github_rn/MainApplication.java @@ -12,6 +12,7 @@ import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; +import com.github_rn.invokenative.DplusReactPackage; import java.util.Arrays; @@ -39,7 +40,8 @@ protected List getPackages() { new RNCWebViewPackage(), new SplashScreenReactPackage(), new VectorIconsPackage(), - new RNGestureHandlerPackage() + new RNGestureHandlerPackage(), + new DplusReactPackage() ); } diff --git a/android/app/src/main/java/com/github_rn/invokenative/AnalyticsModule.java b/android/app/src/main/java/com/github_rn/invokenative/AnalyticsModule.java new file mode 100644 index 0000000..3303065 --- /dev/null +++ b/android/app/src/main/java/com/github_rn/invokenative/AnalyticsModule.java @@ -0,0 +1,199 @@ +package com.github_rn.invokenative; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.json.JSONObject; +import org.json.JSONException; +import java.util.Iterator; + +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.facebook.react.bridge.ReadableNativeMap; +import com.facebook.react.bridge.ReadableType; +import com.umeng.analytics.MobclickAgent; + +/** + * 示例: SDK 接口桥接封装类,并未封装SDK所有API(仅封装常用API接口),设置配置参数类API应在Android原生代码 + * 调用,例如:SDK初始化函数,Log开关函数,子进程自定义事件埋点使能函数,异常捕获功能使能/关闭函数等等。 + * 如果还需要封装其它SDK API,请参考本例自行封装 + * Created by wangfei on 17/8/28. + * -- 适配海棠版(common 2.0.0 + analytics 8.0.0) modify by yujie on 18/12/28 + */ + +public class AnalyticsModule extends ReactContextBaseJavaModule { + private ReactApplicationContext context; + + public AnalyticsModule(ReactApplicationContext reactContext) { + super(reactContext); + context = reactContext; + } + + @Override + public String getName() { + return "UMAnalyticsModule"; + } + + /********************************U-App统计*********************************/ + @ReactMethod + public void onPageStart(String pageName) { + //android.util.Log.e("xxxxxx","onPageStart="+mPageName); + + MobclickAgent.onPageStart(pageName); + } + + @ReactMethod + public void onPageEnd(String pageName) { + //android.util.Log.e("xxxxxx","onPageEnd="+mPageName); + + MobclickAgent.onPageEnd(pageName); + + } + @ReactMethod + public void onEvent(String eventId) { + MobclickAgent.onEvent(context, eventId); + } + @ReactMethod + public void onEventWithLable(String eventId, String eventLabel) { + MobclickAgent.onEvent(context, eventId, eventLabel); + } + @ReactMethod + public void onEventWithMap(String eventId, ReadableMap map) { + Map rMap = new HashMap(); + ReadableMapKeySetIterator iterator = map.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + if (ReadableType.Array == map.getType(key)) { + rMap.put(key, map.getArray(key).toString()); + } else if (ReadableType.Boolean == map.getType(key)) { + rMap.put(key, String.valueOf(map.getBoolean(key))); + } else if (ReadableType.Number == map.getType(key)) { + rMap.put(key, String.valueOf(map.getInt(key))); + } else if (ReadableType.String == map.getType(key)) { + rMap.put(key, map.getString(key)); + } else if (ReadableType.Map == map.getType(key)) { + rMap.put(key, map.getMap(key).toString()); + } + } + MobclickAgent.onEvent(context, eventId, rMap); + } + @ReactMethod + public void onEventWithMapAndCount(String eventId,ReadableMap map,int value) { + Map rMap = new HashMap(); + ReadableMapKeySetIterator iterator = map.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + if (ReadableType.Array == map.getType(key)) { + rMap.put(key, map.getArray(key).toString()); + } else if (ReadableType.Boolean == map.getType(key)) { + rMap.put(key, String.valueOf(map.getBoolean(key))); + } else if (ReadableType.Number == map.getType(key)) { + rMap.put(key, String.valueOf(map.getInt(key))); + } else if (ReadableType.String == map.getType(key)) { + rMap.put(key, map.getString(key)); + } else if (ReadableType.Map == map.getType(key)) { + rMap.put(key, map.getMap(key).toString()); + } + } + MobclickAgent.onEventValue(context, eventId, rMap, value); + } + + @ReactMethod + public void onEventObject(String eventID, ReadableMap property) { + Map map = new HashMap(); + ReadableMapKeySetIterator iterator = property.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + if (ReadableType.Array == property.getType(key)) { + map.put(key, property.getArray(key).toString()); + } else if (ReadableType.Boolean == property.getType(key)) { + map.put(key, String.valueOf(property.getBoolean(key))); + } else if (ReadableType.Number == property.getType(key)) { + map.put(key, String.valueOf(property.getInt(key))); + } else if (ReadableType.String == property.getType(key)) { + map.put(key, property.getString(key)); + } else if (ReadableType.Map == property.getType(key)) { + map.put(key, property.getMap(key).toString()); + } + } + + MobclickAgent.onEventObject(context, eventID, map); + + } + @ReactMethod + public void registerPreProperties(ReadableMap map) { + ReadableNativeMap map2 = (ReadableNativeMap) map; + Map map3 = map2.toHashMap(); + Iterator entries = map3.entrySet().iterator(); + JSONObject json = new JSONObject(); + while (entries.hasNext()) { + Map.Entry entry = (Map.Entry) entries.next(); + String key = (String)entry.getKey(); + String value = (String)entry.getValue(); + try { + json.put(key,value); + }catch (JSONException e){ + + } + } + MobclickAgent.registerPreProperties(context,json); + } + @ReactMethod + public void unregisterPreProperty(String propertyName) { + MobclickAgent.unregisterPreProperty(context, propertyName); + + } + + @ReactMethod + public void getPreProperties(Callback callback) { + String result = MobclickAgent.getPreProperties(context).toString(); + callback.invoke(result); + } + @ReactMethod + public void clearPreProperties() { + MobclickAgent.clearPreProperties(context); + + } + @ReactMethod + public void setFirstLaunchEvent(ReadableArray array) { + List list = new ArrayList(); + for (int i = 0; i < array.size(); i++) { + if (ReadableType.Array == array.getType(i)) { + list.add(array.getArray(i).toString()); + } else if (ReadableType.Boolean == array.getType(i)) { + list.add(String.valueOf(array.getBoolean(i))); + } else if (ReadableType.Number == array.getType(i)) { + list.add(String.valueOf(array.getInt(i))); + } else if (ReadableType.String == array.getType(i)) { + list.add(array.getString(i)); + } else if (ReadableType.Map == array.getType(i)) { + list.add(array.getMap(i).toString()); + } + } + MobclickAgent.setFirstLaunchEvent(context, list); + } + /********************************U-Dplus*********************************/ + @ReactMethod + public void profileSignInWithPUID(String puid) { + MobclickAgent.onProfileSignIn(puid); + } + + @ReactMethod + @SuppressWarnings("unused") + public void profileSignInWithPUIDWithProvider(String provider, String puid) { + MobclickAgent.onProfileSignIn(provider, puid); + } + + @ReactMethod + @SuppressWarnings("unused") + public void profileSignOff() { + MobclickAgent.onProfileSignOff(); + } + +} diff --git a/android/app/src/main/java/com/github_rn/invokenative/DplusReactPackage.java b/android/app/src/main/java/com/github_rn/invokenative/DplusReactPackage.java new file mode 100644 index 0000000..6a9ff0c --- /dev/null +++ b/android/app/src/main/java/com/github_rn/invokenative/DplusReactPackage.java @@ -0,0 +1,39 @@ +package com.github_rn.invokenative; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.shell.MainReactPackage; +import com.facebook.react.uimanager.ViewManager; + +/** + * Created by wangfei on 17/8/28. + */ + +public class DplusReactPackage implements ReactPackage { + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + /** + * 如需要添加本地方法,只需在这里add + * + * @param reactContext + * @return + */ + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new AnalyticsModule(reactContext)); + return modules; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/github_rn/invokenative/RNUMConfigure.java b/android/app/src/main/java/com/github_rn/invokenative/RNUMConfigure.java new file mode 100644 index 0000000..af06a40 --- /dev/null +++ b/android/app/src/main/java/com/github_rn/invokenative/RNUMConfigure.java @@ -0,0 +1,32 @@ +package com.github_rn.invokenative; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build.VERSION_CODES; +import com.umeng.commonsdk.UMConfigure; + +/** + * Created by wangfei on 17/9/14. + */ + +public class RNUMConfigure { + public static void init(Context context, String appkey, String channel, int type, String secret){ + initRN("react-native","2.0"); + UMConfigure.init(context,appkey,channel,type,secret); + } + @TargetApi(VERSION_CODES.KITKAT) + private static void initRN(String v, String t){ + Method method = null; + try { + Class config = Class.forName("com.umeng.commonsdk.UMConfigure"); + method = config.getDeclaredMethod("setWraperType", String.class, String.class); + method.setAccessible(true); + method.invoke(null, v,t); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index 479d3de..c2fbd8e 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -30,6 +30,7 @@ import Ionicons from 'react-native-vector-icons/Ionicons'; //用于页面之间通讯 import EventTypes from '../util/EventTypes'; import EventBus from 'react-native-event-bus'; +import AnalyticsUtil from '../util/AnalyticsUtil'; // import { FLAG_LANGUAGE } from '../expand/dao/LanguageDao'; @@ -83,6 +84,7 @@ export default class Popular extends Component { const {theme} = this.props; return { + AnalyticsUtil.onEvent('SearchClick'); NavigationUtil.goPage({theme}, 'SearchPage') }} > diff --git a/js/page/WelcomePage.js b/js/page/WelcomePage.js index 3133e7c..4ed2778 100644 --- a/js/page/WelcomePage.js +++ b/js/page/WelcomePage.js @@ -2,12 +2,13 @@ import React, {Component} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import NavigationUtil from '../navigator/NavigationUtil'; import SplashScreen from 'react-native-splash-screen' -// import AnalyticsUtil from '../util/AnalyticsUtil'; +import AnalyticsUtil from '../util/AnalyticsUtil'; type Props = {}; export default class Welcome extends Component { componentDidMount() { this.timer = setTimeout(() => { SplashScreen.hide(); + AnalyticsUtil.onPageStart('Welcome'); NavigationUtil.ResetToHomePage(this.props) }, 200); } From 9472c816a68df11d66b432f16b7376c4a70ce544 Mon Sep 17 00:00:00 2001 From: ethan <172529131@qq.com> Date: Thu, 11 Apr 2019 11:39:24 +0800 Subject: [PATCH 16/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9umeng=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 16 +++++++++------- .../main/java/com/github_rn/MainActivity.java | 6 ++---- .../main/java/com/github_rn/MainApplication.java | 14 ++++++++++---- android/app/src/main/res/values/strings.xml | 2 +- js/common/BaseItem.js | 2 ++ js/page/PopularPage.js | 8 +++++++- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3108647..be0bb17 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -6,8 +6,10 @@ - + + + + + - - diff --git a/android/app/src/main/java/com/github_rn/MainActivity.java b/android/app/src/main/java/com/github_rn/MainActivity.java index 862dbf7..4ee01d4 100644 --- a/android/app/src/main/java/com/github_rn/MainActivity.java +++ b/android/app/src/main/java/com/github_rn/MainActivity.java @@ -14,8 +14,6 @@ import com.umeng.analytics.MobclickAgent; import com.umeng.commonsdk.UMConfigure; -// import com.umeng.message.IUmengRegisterCallback; -// import com.umeng.message.PushAgent; public class MainActivity extends ReactActivity { @@ -26,9 +24,9 @@ protected void onCreate(Bundle savedInstanceState) { // 注意:如果您已经在AndroidManifest.xml中配置过appkey和channel值,可以调用此版本初始化函数。 UMConfigure.init(this, "5caac8a63fc1955789000d88", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, null); // interval: 单位是毫秒,默认Session间隔时间是30秒 - MobclickAgent.setSessionContinueMillis(30000); - MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.LEGACY_MANUAL); + UMConfigure.setLogEnabled(true); UMConfigure.setProcessEvent(true); + MobclickAgent.setSessionContinueMillis(30000); } public void onResume() { diff --git a/android/app/src/main/java/com/github_rn/MainApplication.java b/android/app/src/main/java/com/github_rn/MainApplication.java index eef1fd8..8db71ab 100644 --- a/android/app/src/main/java/com/github_rn/MainApplication.java +++ b/android/app/src/main/java/com/github_rn/MainApplication.java @@ -12,7 +12,10 @@ import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; + import com.github_rn.invokenative.DplusReactPackage; +import com.github_rn.invokenative.RNUMConfigure; +import com.umeng.commonsdk.UMConfigure; import java.util.Arrays; @@ -22,10 +25,10 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { - @Override - protected String getJSBundleFile() { - return CodePush.getJSBundleFile(); - } + @Override + protected String getJSBundleFile() { + return CodePush.getJSBundleFile(); + } @Override public boolean getUseDeveloperSupport() { @@ -60,5 +63,8 @@ public ReactNativeHost getReactNativeHost() { public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); + // UMConfigure.setLogEnabled(true); + // UMConfigure.setProcessEvent(true); + // RNUMConfigure.init(this, "5caac8a63fc1955789000d88", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, null); } } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index ad777aa..2010468 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - E_PSonWckd1Pyhp-6okocqPLw21a10f04d24-92c2-41a5-84ae-b434a9f20a91 + Fm8MkfACfLaQ-HnpPtqDGrZPbGt810f04d24-92c2-41a5-84ae-b434a9f20a91 Github_RN diff --git a/js/common/BaseItem.js b/js/common/BaseItem.js index 72a1e26..4da746e 100644 --- a/js/common/BaseItem.js +++ b/js/common/BaseItem.js @@ -2,6 +2,7 @@ import React, {Component} from 'react'; import {StyleSheet,Image, Text, View, TouchableOpacity } from 'react-native'; import FontAwesome from 'react-native-vector-icons/FontAwesome'; import {PropTypes} from 'prop-types'; +import AnalyticsUtil from '../util/AnalyticsUtil'; export default class BaseItem extends Component { static proTypes = { @@ -48,6 +49,7 @@ export default class BaseItem extends Component { }); } onPressFavorite () { + AnalyticsUtil.onEvent('Collection'); this.setFavoriteState(!this.state.isFavorite); this.props.onFavorite(this.props.projectModel.item, !this.state.isFavorite); } diff --git a/js/page/PopularPage.js b/js/page/PopularPage.js index c2fbd8e..0ca5dda 100644 --- a/js/page/PopularPage.js +++ b/js/page/PopularPage.js @@ -60,6 +60,7 @@ export default class Popular extends Component { onLoadLanguage(FLAG_LANGUAGE.flag_key); } componentDidMount() { + AnalyticsUtil.onPageStart('Popular'); } _genTabs () { const tabs = {}; @@ -84,7 +85,12 @@ export default class Popular extends Component { const {theme} = this.props; return { - AnalyticsUtil.onEvent('SearchClick'); + AnalyticsUtil.onEvent('event1'); + // AnalyticsUtil.onEvent('SearchClick'); + // AnalyticsUtil.onEvent('01'); + // AnalyticsUtil.onEventWithMap('SearchClick',{ + // name: 'umeng' + // }); NavigationUtil.goPage({theme}, 'SearchPage') }} > From 3a8531a48f7be9cacac82b0f4b69ad0357209a77 Mon Sep 17 00:00:00 2001 From: songdongdong123 <172529131@qq.com> Date: Thu, 22 Aug 2019 00:21:44 +0800 Subject: [PATCH 17/20] Update package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index b0a552a..84ad8c1 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", + //code-push release-react hui_ying android --t 1.0.0 --dev false --d Production --des "修复" --m true "test": "jest", "debug": "redux-devtools" }, From 9013296794f6b39761e08b9ae13c4d764e47d3e2 Mon Sep 17 00:00:00 2001 From: songdongdong123 <172529131@qq.com> Date: Thu, 22 Aug 2019 00:23:06 +0800 Subject: [PATCH 18/20] Update package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 84ad8c1..b0a552a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", - //code-push release-react hui_ying android --t 1.0.0 --dev false --d Production --des "修复" --m true "test": "jest", "debug": "redux-devtools" }, From 4c03cae26903ca80373ccf30528a0dfe3826e706 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2020 03:40:18 +0000 Subject: [PATCH 19/20] Bump acorn from 5.7.3 to 5.7.4 Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4. - [Release notes](https://github.com/acornjs/acorn/releases) - [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a836464..b372d71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -931,9 +931,9 @@ acorn-walk@^6.0.1: integrity sha1-02O2b1+sXwGP+cOh57b44xDMORM= acorn@^5.5.3: - version "5.7.3" - resolved "http://registry.npm.taobao.org/acorn/download/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha1-Z6ojG/iBKXS4UjWpZ3Hra9B+onk= + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== acorn@^6.0.1: version "6.1.1" From e92c220af1814ffc2f6f37d53ce2fe3071eb541e Mon Sep 17 00:00:00 2001 From: songdongdong123 <172529131@qq.com> Date: Fri, 18 Mar 2022 10:14:08 +0800 Subject: [PATCH 20/20] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f4422d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 songdongdong123 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.