【水滴石穿】imooc_gp
这个项目应该是一个标杆项目,看到之前很有几个项目都是按照这个项目的页面摆放顺序来的
不过可以作为自己做项目的一种方式
源码地址为:https://github.com/pgg-pgg/imooc_gp
项目的数据流是mobx
启动页不是定时器自动跳转那种,而是滑动那种
我们分析代码如下
上面是启动页的
//index.js
/** @format */
import {AppRegistry} from 'react-native';
import setup from "./js/pages/setup"
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => setup);
//js/pages/setup.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, {Component} from 'react';
import {Navigator} from 'react-native-deprecated-custom-components'
import MobxTest from "../MobxTest";
import MyPageTest from "./My/MyPageTest";
import WelcomePage from "./WelcomePage";
function setup() {
//进行一些初始化的配置
class Root extends Component {
renderScene(route, navigator) {
let Component = route.component;
return <Component {...route.params} navigator={navigator}/>
}
render() {
return <Navigator
initialRoute={{component: WelcomePage}}
renderScene={(route, navigator)=>this.renderScene(route, navigator)}
/>
}
}
return <Root/>;
}
module.exports = setup;
首先进入的是welcomePage页面
//js/pages/WelcomePage.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Image,
Text,
View, AsyncStorage,
} from 'react-native';
import NavigationBar from "../common/NavigationBar"
import HomePage from "./HomePage"
import GuidePage from "./GuidePage";
import ThemeDao from "../expand/dao/ThemeDao";
import SplashScreen from 'react-native-splash-screen'
export default class WelcomePage extends Component{
openApp(){
//进行了判断是不是第一次打开
AsyncStorage.getItem('isFirst',(error,result)=>{
if (result === 'false') {
console.log('不是第一次打开');
this.props.navigator.resetTo({
//不是第一次打开就是进入home页面
component:HomePage,
params:{
theme:this.theme,
...this.props
}
})
} else {
console.log('第一次打开');
//是第一次打开就进入导航页
this.props.navigator.replace({
component:GuidePage,
params:{
theme:this.theme,
...this.props
}
})
}
});
}
componentDidMount(): void {
new ThemeDao().getTheme().then(data=>{
this.theme = data;
})
this.timer = setTimeout(()=>{
SplashScreen.hide();
this.openApp()
},1000)
}
componentWillUnmount(): void {
this.timer&&clearTimeout(this.timer);
}
constructor(props){
super(props)
}
render(){
return null;
}
}
第一次打开进入的导航页面
//GuidePage
import {StyleSheet, View, Button, Text, Image, TouchableOpacity,AsyncStorage} from 'react-native';
import React, {Component} from 'react';
import {PagerTabIndicator, IndicatorViewPager, PagerTitleIndicator, PagerDotIndicator} from 'rn-viewpager';
import HomePage from "./HomePage";
export default class GuidePage extends Component {
render() {
let text1 = '消费者第一\n合作伙伴第二\nQunar第三';
let text2 = '大声说话\n无\'总\'称谓\n遇到批评三不问';
let text3 = '高层不诿过\n中层不放羊\n基层不跳步';
return (
<View style={{flex: 1}}>
<IndicatorViewPager
style={{flex: 1}}
indicator={GuidePage._renderDotIndicator()}>
<View style={{backgroundColor: '#ff8800', justifyContent: 'center'}}>
<Image resizeMode={'contain'} style={styles.image}
source={require('../../res/images/s_0_1.png')}/>
<Text style={styles.text_desc}>{text1}</Text>
</View>
<View style={{backgroundColor: '#669900', justifyContent: 'center'}}>
<Image resizeMode={'contain'} style={styles.image}
source={require('../../res/images/s_1_1.png')}/>
<Text style={styles.text_desc}>{text2}</Text>
</View>
<View style={{backgroundColor: '#aa66cc', justifyContent: 'center'}}>
<Image resizeMode={'contain'} style={styles.image}
source={require('../../res/images/s_2_1.png')}/>
<Text style={styles.text_desc}>{text3}</Text>
<TouchableOpacity onPress={()=>{
// 存储
AsyncStorage.setItem('isFirst','false',(error)=>{
if (error) {
alert(error);
}
});
this.props.navigator.resetTo({
component:HomePage,
params:{
// theme:this.props.theme,
...this.props
}
})
}}>
<Text style={styles.text}>开始使用</Text>
</TouchableOpacity>
</View>
</IndicatorViewPager>
</View>
);
}
static _renderDotIndicator() {
return <PagerDotIndicator pageCount={3}/>;
}
}
const styles = StyleSheet.create({
text: {
alignSelf: 'center',
padding: 5,
backgroundColor: '#2196f3',
borderRadius: 5,
fontSize: 18,
color: 'white',
},
text_desc: {
height:200,
textAlign: 'center',
textAlignVertical:'center',
fontSize: 20,
color: 'white',
alignSelf: 'center',
},
image: {
width: 200,
height: 200,
alignSelf: 'center'
},
btn: {
width: 150,
height: 40,
backgroundColor: '#1296db',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 50
},
btnText: {
fontSize: 18,
color: '#fff'
},
});
//js/pages/HomePage.js
//里面的tab切换,根据选中的跳转进去不同的页面
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Image,
Text,
View,
DeviceEventEmitter,
} from 'react-native';
import TabNavigator from 'react-native-tab-navigator'
import PopularPage from "./popular/PopularPage";
import Toast ,{DURATION}from "react-native-easy-toast";
import TrendingPage from "./trending/TrendingPage";
import FavoritePage from "./favorite/FavoritePage";
import MyPage from "./My/MyPage";
import BaseComponent from "./BaseComponent";
import codePush from "react-native-code-push";
export const ACTION_HOME = {A_SHOW_TOAST:'showToast',A_RESTART:'restart',A_THEME:'THEME'};
export const FLAG_TAB={
flag_popularTab:'tb_popular',
flag_trendingTab:'tb_trending',
flag_favoriteTab:'tb_favorite',
flag_myTab:'tb_my',
}
export default class HomePage extends BaseComponent {
constructor(props) {
super(props);
let selectedTab=this.props.selectedTab?this.props.selectedTab:'tb_popular';
this.state = {
selectedTab: selectedTab,
theme:this.props.theme,
}
}
componentDidMount(): void {
super.componentDidMount();
this.listener = DeviceEventEmitter.
addListener('ACTION_HOME',
(action,params)=>this.onAction(action,params));
this.update();
}
/**
* 想codepush检查更新
*/
update(){
codePush.sync({
updateDialog:{
appendReleaseDescription:true,
descriptionPrefix:'更新内容',
title:'更新',
mandatoryUpdateMessage:'',
mandatoryContinueButtonLabel:'更新',
},
mandatoryInstallMode:codePush.InstallMode.ON_NEXT_RESTART,
});
}
/**
* 通知回调事件处理
* @param action
* @param params
*/
onAction(action,params){
if(ACTION_HOME.A_RESTART===action){
this.onRestart(params)
}else if(ACTION_HOME.A_SHOW_TOAST===action){
this.toast.show(params.text,DURATION.LENGTH_SHORT);
}
}
/**
* 重启首页
* @param jumpToTab
*/
onRestart(jumpToTab){
this.props.navigator.resetTo({
component:HomePage,
params:{
...this.props,
selectedTab:jumpToTab
}
})
}
componentWillUnmount(): void {
super.componentWillUnmount();
if (this.listener) {
this.listener.remove();
}
}
_renderType(Component,selectTab,title,renderIcon){
return <TabNavigator.Item
selected={this.state.selectedTab === selectTab}
selectedTitleStyle={this.state.theme.styles.selectedTitleStyle}
title={title}
renderIcon={() => <Image style={styles.image} source={renderIcon}/>}
renderSelectedIcon={() => <Image style={[styles.image,this.state.theme.styles.tabBarSelectedIcon]} source={renderIcon}/>}
// badgeText="1"
onPress={() => this.setState({selectedTab: selectTab})}>
<View style={styles.page1}>
<Component {...this.props} theme={this.state.theme}/>
</View>
</TabNavigator.Item>
}
render() {
return (
// tab切换
<View style={styles.container}>
<TabNavigator>
{this._renderType(PopularPage,'tb_popular','最热',require('../../res/images/ic_polular.png'))}
{this._renderType(TrendingPage,'tb_trending','趋势',require('../../res/images/ic_trending.png'))}
{this._renderType(FavoritePage,'tb_favorite','收藏',require('../../res/images/ic_favorite.png'))}
{this._renderType(MyPage,'tb_my','我的',require('../../res/images/ic_my.png'))}
</TabNavigator>
<Toast ref={toast=>this.toast=toast}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
page1:{
flex: 1,
},
page2:{
flex: 2,
backgroundColor: 'green'
},
image:{
width:22,
height:22
}
});
//其实挺有意思的
tab首先的页面
应该只是布局,然后从后端请求数据渲染即可
代码如下:
//js/pages/popular/PopularPage.js
import React, {Component} from 'react';
import {
Image,
StyleSheet, TouchableOpacity,
View,
} from 'react-native';
import NavigationBar from "../../common/NavigationBar";
import ScrollableTabView, {ScrollableTabBar} from 'react-native-scrollable-tab-view'
import LanguageDao, {FLAG_LANGUAGE} from '../../expand/dao/LanguageDao'
import PopularTab from "./PopularTab";
import ViewUtils from "../../util/ViewUtils";
import SearchPage from "../SearchPage";
import {MORE_MENU} from "../../common/MoreMenu";
import {FLAG_TAB} from "../HomePage";
import MoreMenu from "../../common/MoreMenu";
import BaseComponent from "../BaseComponent";
import CustomTheme from "../My/CustomTheme";
export default class PopularPage extends BaseComponent {
constructor(props) {
super(props);
this.languageDao = new LanguageDao(FLAG_LANGUAGE.flag_key);
this.state = {
languages: [],
customThemeViewVisible: false,
theme:this.props.theme,
}
}
componentDidMount() {
super.componentDidMount()
//渲染数据
this.loadData();
}
loadData() {
//取出数据
this.languageDao.fetch()
.then(result => {
this.setState({
languages: result
})
}).catch(error => {
console.log(error)
})
}
//点击不同的语言界面,最后是进入不同的语言列表新闻中
renderRightButton() {
return <View style={{flexDirection:'row'}}>
<TouchableOpacity
onPress={()=> {
this.props.navigator.push({
component:SearchPage,
params:{
...this.props
}
})
}}>
<View style={{padding:5,marginRight:8}}>
<Image
style={{width:24,height:24}}
source={require('../../../res/images/ic_search_white_48pt.png')}
/>
</View>
</TouchableOpacity>
{ViewUtils.getMoreButton(()=>this.refs.moreMenu.open())}
</View>
}
renderMoreView(){
let params={...this.props,fromPage:FLAG_TAB.flag_popularTab};
return <MoreMenu
ref="moreMenu"
{...params}
menus={[MORE_MENU.Custom_Key,MORE_MENU.Sort_Key,MORE_MENU.Remove_Key,MORE_MENU.Share,MORE_MENU.Custom_Theme,
MORE_MENU.About_Author,MORE_MENU.About]}
anchorView={()=>this.refs.moreMenuButton}
onMoreMenuSelect={(e)=> {
if (e === MORE_MENU.Custom_Theme) {
this.setState({
customThemeViewVisible:true
})
}
}}
/>
}
renderCustomThemeView() {
return (
<CustomTheme visible={this.state.customThemeViewVisible}
{...this.props}
onClose={() => {
this.setState({
customThemeViewVisible: false,
})
}}/>
)
}
render() {
let statusBar = {
backgroundColor:this.state.theme.themeColor,
barStyle:'light-content',
};
//判断语言切换的,这边用到的是ScrollableTabView组件
let content = this.state.languages.length > 0 ? <ScrollableTabView
tabBarBackgroundColor={this.state.theme.themeColor}
tabBarActiveTextColor='#fff'
tabBarInactiveTextColor='#333'
tabBarUnderlineStyle={{backgroundColor: '#e7e7e7', height: 2}}
renderTabBar={() => <ScrollableTabBar/>}>
{this.state.languages.map((result, i, arr) => {
let lan = arr[i];
return lan.checked ?
<PopularTab key={i} tabLabel={lan.name} {...this.props}/>: null;
})}
</ScrollableTabView> : null;
//底部图标切换
return <View style={styles.container}>
<NavigationBar
title={'最热'}
leftButton={<View/>}
rightButton={this.renderRightButton()}
statusBar={statusBar}
style={this.state.theme.styles.navBar}/>
{content}
{this.renderMoreView()}
{this.renderCustomThemeView()}
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
});
//js/pages/popular/PopularTab.js
//下拉刷新和获取数据
import React, {Component} from 'react';
import {DeviceEventEmitter, ListView, RefreshControl, StyleSheet, View} from "react-native";
import ProjectModel from "../../model/ProjectModel";
import Utils from "../../util/Utils";
import DescPage from "./DescPage";
import {FLAG_STORAGE} from "../../expand/dao/DataRepository";
import RepositoryCell from "../../common/RepositoryCell";
import FavoriteDao from "../../expand/dao/FavoriteDao";
import DataRepository from "../../expand/dao/DataRepository";
import ActionUtils from "../../util/ActionUtils";
const URL = "https://api.github.com/search/repositories?q=";
const QUERY_STR = '&sort=stars';
const favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_popular);
const dataRepository = new DataRepository(FLAG_STORAGE.flag_popular);
export default class PopularTab extends Component {
constructor(props) {
super(props);
this.isFavoriteChange=false;
this.state = {
result: '',
isLoading: false,
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
favoriteKeys :[],
theme:this.props.theme,
}
}
componentDidMount(): void {
this.onLoad();
this.listener= DeviceEventEmitter.addListener('favoriteChanged_popular',()=>{
this.isFavoriteChange = true;
})
}
componentWillUnmount(): void {
if (this.listener){
this.listener.remove();
}
}
componentWillReceiveProps(nextProps: Readonly<P>, nextContext: any): void {
if (this.isFavoriteChange) {
this.isFavoriteChange=false;
this.getFavoriteKeys()
}else if (nextProps.theme !== this.state.theme) {
this.updateState({
theme:nextProps.theme,
})
this.flushFavoriteState()
}
}
/**
* 更新project item收藏状态
*/
flushFavoriteState(){
let projectModels = [];
let items = this.items;
for(let i =0 ,len = items.length;i<len;i++){
projectModels.push(new ProjectModel(items[i],Utils.checkFavorite(items[i],this.state.favoriteKeys)))
}
this.updateState({
isLoading:false,
dataSource:this.getDataSource(projectModels),
})
}
getDataSource(data){
return this.state.dataSource.cloneWithRows(data);
}
getFavoriteKeys(){
favoriteDao.getFavoriteKeys()
.then(keys=>{
if (keys) {
this.updateState({
favoriteKeys: keys
})
}
this.flushFavoriteState();
})
.catch(e=>{
this.flushFavoriteState()
})
}
updateState(dic){
if (!this) return;
this.setState(dic);
}
onLoad() {
this.updateState({
isLoading: true
});
let url = URL + this.props.tabLabel + QUERY_STR;
dataRepository.fetchRepository(url)
.then(result => {
this.items = result && result.items ? result.items : result ? result : [];
this.getFavoriteKeys();
if (!this.items||result && result.update_date && !dataRepository.checkData(result.update_date)) {
return dataRepository.fetchNetRepository(url)
}
})
.then(items => {
if (!items || items.length === 0) {
return;
} else {
this.items=items;
this.getFavoriteKeys()
}
})
.catch(error => {
console.log(error);
this.updateState({
isLoading:false,
})
});
}
choose(projectModel) {
return <RepositoryCell
onSelect={()=>ActionUtils.onSelectRepository({
flag: FLAG_STORAGE.flag_popular,
...this.props,
projectModel: projectModel,
})}
theme={this.state.theme}
key={projectModel.item.id}
onFavorite={(item,isFavorite)=>ActionUtils.onFavorite(favoriteDao,item, isFavorite)}
projectModel={projectModel}/>
}
render() {
return <View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={(data) => this.choose(data)}
refreshControl={
<RefreshControl
refreshing={this.state.isLoading}
onRefresh={() => this.onLoad()}
color={this.state.theme.themeColor}
tintColor={this.state.theme.themeColor}
title={'Loading...'}
titleColor={this.state.theme.themeColor}
/>
}
/>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
page1: {
flex: 1,
backgroundColor: 'red'
},
page2: {
flex: 2,
backgroundColor: 'green'
},
image: {
width: 22,
height: 22
},
item: {
backgroundColor: '#169',
height: 100,
margin: 15,
alignItems: 'center',
justifyContent: 'center',
},
text_my: {
color: 'black',
fontSize: 20,
},
text: {
color: 'white',
fontSize: 20,
},
indicatorContainer: {
alignItems: 'center'
},
indicator: {
color: 'red',
margin: 10
}
});
//js/pages/popular/store/Store.js
import {observable} from "mobx";
import DataRepository from "../../../expand/dao/DataRepository";
export default class PopularStore {
api;
@observable popularItems=[];
constructor(){
this.api=new DataRepository();
}
getPopularItem(id){
return this.popularItems.find(item=>item.id.toString()===id);
}
@action.bound()
fetchPopularList(tabLabel){
return this.api.getPopularInfo(tabLabel)
.then(action(result => {
this.items = result && result.items ? result.items : result ? result : [];
this.getFavoriteKeys();
if (!this.items||result && result.update_date && !dataRepository.checkData(result.update_date)) {
return dataRepository.fetchNetRepository(url)
}
})
.then(items => {
if (!items || items.length === 0) {
return;
} else {
this.items=items;
this.getFavoriteKeys()
}
})
.catch(error => {
console.log(error);
this.updateState({
isLoading:false,
})
}));
}
}
//js/pages/popular/model/ProjectModel.js
import {observable} from "mobx";
export default class ProjectModel{
@observable item;
@observable isFavorite;
}
//js/pages/popular/DescPage.js
import React, {Component} from 'react'
import {
View,
StyleSheet,
WebView,
TouchableOpacity,
Image,
} from 'react-native'
import NavigationBar from "../../common/NavigationBar";
import ViewUtils from "../../util/ViewUtils";
import FavoriteDao from "../../expand/dao/FavoriteDao";
import share from "../../../res/data/share";
import UShare from "../../common/UShare";
import BackPressComponent from "../../common/BackPressComponent";
const TRENDING_URL = 'https://github.com/';
export default class DescPage extends Component {
constructor(props) {
super(props);
// alert(this.props.title);
this.backPress = new BackPressComponent({backPress:(e)=>this.onBackPress(e)});
this.url = this.props.projectModel.item.html_url ? this.props.projectModel.item.html_url : TRENDING_URL + this.props.projectModel.item.fullName;
let title = this.props.projectModel.item.full_name ? this.props.projectModel.item.full_name : this.props.projectModel.item.fullName;
this.favoriteDao = new FavoriteDao(this.props.flag);
this.state = {
url: this.url,
title: title,
canGoBack: false,
isFavorite: this.props.projectModel.isFavorite,
favoriteIcon: this.props.projectModel.isFavorite ? require('../../../res/images/ic_star.png')
: require('../../../res/images/ic_star_navbar.png'),
theme: this.props.theme,
}
}
componentDidMount(): void {
this.backPress.componentDidMount();
}
componentWillUnmount(): void {
this.backPress.componentWillUnmount();
if (this.props.onUpdateFavorite)this.props.onUpdateFavorite();
}
onBack() {
if (this.state.canGoBack) {
this.webView.goBack()
} else {
this.props.navigator.pop();
}
}
onBackPress(e){
this.onBack();
return true;
}
go() {
this.setState({
url: this.text
})
}
onNavigationStateChange(e) {
this.setState({
canGoBack: e.canGoBack,
url: e.url,
})
}
onRightButtonClick() {
let projectModel = this.props.projectModel;
this.setFavoriteState(projectModel.isFavorite = !projectModel.isFavorite);
const key = projectModel.item.fullName ? projectModel.item.fullName : projectModel.item.id.toString();
if (projectModel.isFavorite) {
this.favoriteDao.saveFavoriteItem(key, JSON.stringify(projectModel.item))
} else {
this.favoriteDao.removeFavoriteItem(key)
}
}
setFavoriteState(isFavorite) {
this.setState({
isFavorite: isFavorite,
favoriteIcon: isFavorite ? require('../../../res/images/ic_star.png')
: require('../../../res/images/ic_star_navbar.png')
})
}
renderRightButton() {
return <View style={{flexDirection: 'row'}}>
{ViewUtils.getShareButton(()=>this.shareInfo())}
<TouchableOpacity onPress={() => this.onRightButtonClick()}>
<Image style={{width: 20, height: 20, marginRight: 10}} source={this.state.favoriteIcon}/>
</TouchableOpacity>
</View>
}
shareInfo(){
const shareApp = share.share_app;
UShare.share(shareApp.title, shareApp.content,
shareApp.imgUrl,shareApp.url,()=>{},()=>{});
}
render() {
let statusBar = {
backgroundColor: this.state.theme.themeColor
};
let titleLayoutStyle = this.state.title.length>20?{paddingRight: 30}:null;
return <View style={styles.container}>
<NavigationBar
title={this.state.title}
statusBar={statusBar}
titleLayoutStyle={titleLayoutStyle}
style={this.state.theme.styles.navBar}
rightButton={this.renderRightButton()}
leftButton={ViewUtils.getLeftButton(() => this.onBack())}
/>
<WebView ref={webView => this.webView = webView}
source={{uri: this.state.url}}
startInLoadingState={true}
onNavigationStateChange={(e) =>
this.onNavigationStateChange(e)}>
</WebView>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
tips: {
fontSize: 20
},
row: {
flexDirection: 'row',
alignItems: 'center',
margin: 10
},
input: {
height: 40,
flex: 1,
borderWidth: 1,
margin: 2,
}
});
趋势页面跟最热页面类似
//js/pages/trending/TrendingPage.js
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Image,
Text,
View,
TextInput,
ListView,
RefreshControl,
DeviceEventEmitter,
TouchableOpacity,
} from 'react-native';
import NavigationBar from "../../common/NavigationBar"
import DataRepository, {FLAG_STORAGE} from "../../expand/dao/DataRepository"
import ScrollableTabView, {ScrollableTabBar} from 'react-native-scrollable-tab-view'
import RepositoryCell from "../../common/RepositoryCell";
import LanguageDao, {FLAG_LANGUAGE} from '../../expand/dao/LanguageDao'
import DescPage from "../popular/DescPage";
import TrendingCell from "../../common/TrendingCell";
import TimeSpan from "../../model/TimeSpan";
import Popover from "../../common/Popover";
import FavoriteDao from "../../expand/dao/FavoriteDao";
import ProjectModel from "../../model/ProjectModel";
import Utils from "../../util/Utils";
import ActionUtils from "../../util/ActionUtils";
import {FLAG_TAB} from "../HomePage";
import MoreMenu, {MORE_MENU} from "../../common/MoreMenu";
import ViewUtils from "../../util/ViewUtils";
import BaseComponent from "../BaseComponent";
import CustomTheme from "../My/CustomTheme";
const API_URL = "https://github.com/trending/";
var timeSpanTextArray = [new TimeSpan('今 天', 'since=daily'), new TimeSpan('本 周', 'since=weekly'), new TimeSpan('本 月', 'since=monthly')];
var favoriteDao = new FavoriteDao(FLAG_STORAGE.flag_trending);
var dataRespository = new DataRepository(FLAG_STORAGE.flag_trending);
export default class TrendingPage extends BaseComponent {
constructor(props) {
super(props);
this.languageDao = new LanguageDao(FLAG_LANGUAGE.flag_language);
this.state = {
languages: [],
isVisible: false,
buttonRect: {},
timeSpan: timeSpanTextArray[0],
theme:this.props.theme,
customThemeViewVisible: false,
}
}
componentDidMount() {
super.componentDidMount();
this.loadData();
}
componentWillReceiveProps(nextProps: Readonly<P>, nextContext: any): void {
if (nextProps.timeSpan !== this.props.timeSpan) {
this.loadData(nextProps.timeSpan)
}
}
loadData() {
this.languageDao.fetch()
.then(result => {
this.setState({
languages: result
})
}).catch(error => {
console.log(error)
})
}
showPopover() {
this.refs.button.measure((ox, oy, width, height, px, py) => {
this.setState({
isVisible: true,
buttonRect: {x: px, y: py, width: width, height: height}
});
});
}
closePopover() {
this.setState({
isVisible: false,
});
}
renderTitleView() {
return <View>
<TouchableOpacity ref={'button'} onPress={() => {
this.showPopover()
}}>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={{
fontSize: 18,
color: 'white',
fontWeight: '400'
}}>趋势 {this.state.timeSpan.showText}</Text>
<Image style={{width: 12, height: 12, marginLeft: 5}}
source={require('../../../res/images/ic_spinner_triangle.png')}/>
</View>
</TouchableOpacity>
</View>
}
onSelectTimeSpan(timeSpan) {
this.setState({
timeSpan: timeSpan,
isVisible: false,
})
}
renderMoreView() {
let params = {...this.props, fromPage: FLAG_TAB.flag_popularTab};
return <MoreMenu
ref="moreMenu"
{...params}
menus={[MORE_MENU.Custom_Language, MORE_MENU.Sort_Language,MORE_MENU.Share, MORE_MENU.Custom_Theme,
MORE_MENU.About_Author, MORE_MENU.About]}
anchorView={() => this.refs.moreMenuButton}
onMoreMenuSelect={(e) => {
if (e === MORE_MENU.Custom_Theme) {
this.setState({
customThemeViewVisible: true
})
}
}}
/>
}
renderCustomThemeView() {
return (
<CustomTheme visible={this.state.customThemeViewVisible}
{...this.props}
onClose={() => {
this.setState({
customThemeViewVisible: false,
})
}}/>
)
}
render() {
let statusBar = {
backgroundColor:this.state.theme.themeColor
};
let content = this.state.languages.length > 0 ? <ScrollableTabView
tabBarBackgroundColor={this.state.theme.themeColor}
tabBarActiveTextColor='#fff'
tabBarInactiveTextColor='#333'
tabBarUnderlineStyle={{backgroundColor: '#e7e7e7', height: 2}}
renderTabBar={() => <ScrollableTabBar/>}>
{this.state.languages.map((result, i, arr) => {
let lan = arr[i];
return lan.checked ?
<TrendingTab key={i} tabLabel={lan.name}
timeSpan={this.state.timeSpan} {...this.props}>{lan.name}</TrendingTab> : null;
})}
</ScrollableTabView> : null;
let timeSpanView = <Popover
isVisible={this.state.isVisible}
fromRect={this.state.buttonRect}
placement={'bottom'}
contentStyle={{backgroundColor: '#343434', opacity: 0.5}}
onClose={() => this.closePopover()}>
{timeSpanTextArray.map((result, i, arr) => {
return <TouchableOpacity key={i} underlayColor={'transparent'}
onPress={() => this.onSelectTimeSpan(arr[i])}>
<Text style={{fontSize: 18, color: 'white', padding: 8, fontWeight: '400'}}>{arr[i].showText}</Text>
</TouchableOpacity>
})}
</Popover>;
return <View style={styles.container}>
<NavigationBar
titleView={this.renderTitleView()}
leftButton={<View/>}
rightButton={ViewUtils.getMoreButton(() => this.refs.moreMenu.open())}
statusBar={statusBar}
style={this.state.theme.styles.navBar}/>
{content}
{timeSpanView}
{this.renderMoreView()}
{this.renderCustomThemeView()}
</View>
}
}
class TrendingTab extends Component {
constructor(props) {
super(props);
this.isFavoriteChange = false;
this.state = {
result: '',
isLoading: false,
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
favoriteKeys: [],
theme:this.props.theme,
}
}
/**
* 更新project item收藏状态
*/
flushFavoriteState() {
let projectModels = [];
let items = this.items;
for (let i = 0, len = items.length; i < len; i++) {
projectModels.push(new ProjectModel(items[i], Utils.checkFavorite(items[i], this.state.favoriteKeys)))
}
this.updateState({
isLoading: false,
dataSource: this.getDataSource(projectModels),
})
}
getDataSource(data) {
return this.state.dataSource.cloneWithRows(data);
}
getFavoriteKeys() {
favoriteDao.getFavoriteKeys()
.then(keys => {
if (keys) {
this.updateState({
favoriteKeys: keys
})
}
this.flushFavoriteState();
})
.catch(e => {
this.flushFavoriteState()
})
}
updateState(dic) {
if (!this) return;
this.setState(dic);
}
componentDidMount(): void {
this.onLoad(this.props.timeSpan, true)
this.listener = DeviceEventEmitter.addListener('favoriteChanged_trending', () => {
this.isFavoriteChange = true;
})
}
onUpdateFavorite() {
this.getFavoriteKeys();
}
componentWillUnmount(): void {
if (this.listener) {
this.listener.remove();
}
}
componentWillReceiveProps(nextProps: Readonly<P>, nextContext: any): void {
if (this.isFavoriteChange) {
this.isFavoriteChange = false;
this.getFavoriteKeys()
}else if (nextProps.theme !== this.state.theme) {
this.updateState({
theme:nextProps.theme,
});
this.flushFavoriteState()
}
}
getFetchUrl(timeSpan, category) {
return API_URL + category + '?' + timeSpan.searchText
}
onLoad(timeSpan, isRefresh) {
this.updateState({
isLoading: true
});
let url = this.getFetchUrl(timeSpan, this.props.tabLabel)
dataRespository.fetchRepository(url)
.then(result => {
this.items = result && result.items ? result.items : result ? result : [];
this.getFavoriteKeys();
if (!this.items || isRefresh && result && result.update_date && !dataRespository.checkData(result.update_date)) {
return dataRespository.fetchNetRepository(url)
}
})
.then(items => {
if (!items || items.length === 0) {
return;
} else {
this.items = items;
this.getFavoriteKeys();
}
})
.catch(error => {
this.updateState({
isLoading: false
})
});
}
choose(projectModel) {
return <TrendingCell
onSelect={() => ActionUtils.onSelectRepository({
projectModel: projectModel,
flag: FLAG_STORAGE.flag_trending,
...this.props,
parentComponent: this,
onUpdateFavorite: ()=>this.onUpdateFavorite(),
})}
theme={this.props.theme}
key={projectModel.item.fullName}
onFavorite={(item, isFavorite) => ActionUtils.onFavorite(favoriteDao, item, isFavorite, FLAG_STORAGE.flag_trending)}
projectModel={projectModel}/>
}
onRefresh() {
this.onLoad(this.props.timeSpan, true);
}
render() {
return <View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={(data) => this.choose(data)}
refreshControl={
<RefreshControl
refreshing={this.state.isLoading}
onRefresh={() => this.onRefresh()}
color={this.state.theme.themeColor}
tintColor={this.state.theme.themeColor}
title={'Loading...'}
titleColor={this.state.theme.themeColor}
/>
}
/>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
page1: {
flex: 1,
backgroundColor: 'red'
},
page2: {
flex: 2,
backgroundColor: 'green'
},
image: {
width: 22,
height: 22
},
item: {
backgroundColor: '#169',
height: 100,
margin: 15,
alignItems: 'center',
justifyContent: 'center',
},
text_my: {
color: 'black',
fontSize: 20,
},
text: {
color: 'white',
fontSize: 20,
},
indicatorContainer: {
alignItems: 'center'
},
indicator: {
color: 'red',
margin: 10
}
});
//收藏页面
//js/pages/favorite/FavoritePage.js
import React, {Component} from 'react';
import {
StyleSheet,
View,
ListView,
RefreshControl,
DeviceEventEmitter,
} from 'react-native';
import NavigationBar from "../../common/NavigationBar"
import {FLAG_STORAGE} from "../../expand/dao/DataRepository"
import ScrollableTabView, {ScrollableTabBar} from 'react-native-scrollable-tab-view'
import RepositoryCell from "../../common/RepositoryCell";
import DescPage from "../popular/DescPage";
import ProjectModel from "../../model/ProjectModel";
import FavoriteDao from "../../expand/dao/FavoriteDao";
import TrendingCell from "../../common/TrendingCell";
import ArrayUtils from "../../util/ArrayUtils";
import ActionUtils from "../../util/ActionUtils";
import MoreMenu, {MORE_MENU} from "../../common/MoreMenu";
import {FLAG_TAB} from "../HomePage";
import ViewUtils from "../../util/ViewUtils";
import BaseComponent from "../BaseComponent";
import CustomTheme from "../My/CustomTheme";
export default class FavoritePage extends BaseComponent {
constructor(props) {
super(props);
this.state = {
theme: this.props.theme,
customThemeViewVisible: false,
}
}
renderMoreView() {
let params = {...this.props, fromPage: FLAG_TAB.flag_popularTab}
return <MoreMenu
ref="moreMenu"
{...params}
menus={[MORE_MENU.Custom_Theme, MORE_MENU.Share,
MORE_MENU.About_Author, MORE_MENU.About]}
anchorView={() => this.refs.moreMenuButton}
onMoreMenuSelect={(e) => {
if (e === MORE_MENU.Custom_Theme) {
this.setState({
customThemeViewVisible: true
})
}
}}
/>
}
renderCustomThemeView() {
return (
<CustomTheme visible={this.state.customThemeViewVisible}
{...this.props}
onClose={() => {
this.setState({
customThemeViewVisible: false,
})
}}/>
)
}
render() {
let statusBar = {
backgroundColor: this.state.theme.themeColor
};
let content = <ScrollableTabView
tabBarBackgroundColor={this.state.theme.themeColor}
tabBarActiveTextColor='#fff'
tabBarInactiveTextColor='#333'
tabBarUnderlineStyle={{backgroundColor: '#e7e7e7', height: 2}}
renderTabBar={() => <ScrollableTabBar/>}
>
<FavoriteTab tabLabel='最热' flag={FLAG_STORAGE.flag_popular} {...this.props}/>
<FavoriteTab tabLabel='趋势' flag={FLAG_STORAGE.flag_trending} {...this.props}/>
</ScrollableTabView>
return <View style={styles.container}>
<NavigationBar
title={'收藏'}
leftButton={<View/>}
rightButton={ViewUtils.getMoreButton(() => this.refs.moreMenu.open())}
statusBar={statusBar}
style={this.state.theme.styles.navBar}/>
{content}
{this.renderMoreView()}
{this.renderCustomThemeView()}
</View>
}
}
class FavoriteTab extends Component {
constructor(props) {
super(props);
this.unFavoriteItems = [];
this.favoriteDao = new FavoriteDao(this.props.flag);
this.state = {
result: '',
isLoading: false,
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
favoriteKeys: [],
theme: this.props.theme,
}
}
componentDidMount(): void {
this.onLoad(true)
}
componentWillReceiveProps(nextProps: Readonly<P>, nextContext: any): void {
this.onLoad(false)
}
updateState(dic) {
if (!this) return;
this.setState(dic);
}
getDataSource(data) {
return this.state.dataSource.cloneWithRows(data);
}
onLoad(isShowLoading) {
if (isShowLoading) {
this.updateState({
isLoading: true
});
}
this.favoriteDao.getAllItems()
.then(items => {
let resultData = [];
for (let i = 0, len = items.length; i < len; i++) {
resultData.push(new ProjectModel(items[i], true));
}
this.updateState({
isLoading: false,
dataSource: this.getDataSource(resultData)
})
})
.catch(e => {
this.updateState({
isLoading: false
})
})
}
choose(projectModel) {
let CellComponent = this.props.flag === FLAG_STORAGE.flag_popular ? RepositoryCell : TrendingCell;
return <CellComponent
onSelect={() => ActionUtils.onSelectRepository({
flag: FLAG_STORAGE.flag_popular,
...this.props,
projectModel: projectModel,
})}
theme={this.props.theme}
key={this.props.flag === FLAG_STORAGE.flag_popular ? projectModel.item.id : projectModel.item.fullName}
onFavorite={(item, isFavorite) => ActionUtils.onFavorite(this.favoriteDao, item, isFavorite, this.props.flag)}
projectModel={projectModel}/>
}
render() {
return <View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={(data) => this.choose(data)}
enableEmptySections={true}
refreshControl={
<RefreshControl
refreshing={this.state.isLoading}
onRefresh={() => this.onLoad()}
color={this.state.theme.themeColor}
tintColor={this.state.theme.themeColor}
title={'Loading...'}
titleColor={this.state.theme.themeColor}
/>
}
/>
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
page1: {
flex: 1,
backgroundColor: 'red'
},
page2: {
flex: 2,
backgroundColor: 'green'
},
image: {
width: 22,
height: 22
},
item: {
backgroundColor: '#169',
height: 100,
margin: 15,
alignItems: 'center',
justifyContent: 'center',
},
text_my: {
color: 'black',
fontSize: 20,
},
text: {
color: 'white',
fontSize: 20,
},
indicatorContainer: {
alignItems: 'center'
},
indicator: {
color: 'red',
margin: 10
}
});
//js/pages/My/MyPage.js
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ScrollView,
TouchableHighlight,
Image,
} from 'react-native';
import NavigationBar from "../../common/NavigationBar";
import GlobalStyles from "../../../res/styles/GlobalStyles"
import ViewUtils from "../../util/ViewUtils";
import CustomKeyPage from "./CustomKeyPage";
import {FLAG_LANGUAGE} from "../../expand/dao/LanguageDao";
import SortKeyPage from "./SortKeyPage";
import AboutMePage from "./about/AboutMePage";
import AboutPage from "./about/AboutPage";
import {MORE_MENU} from "../../common/MoreMenu";
import CustomTheme from "./CustomTheme";
import BaseComponent from "../BaseComponent";
import codePush from "react-native-code-push";
export default class MyPage extends BaseComponent {
constructor(props) {
super(props);
this.state = {
customThemeViewVisible: false,
theme:this.props.theme,
}
}
onClick(tab) {
let TargetComponent, params = {...this.props, menuType: tab};
switch (tab) {
case MORE_MENU.Custom_Language:
TargetComponent = CustomKeyPage;
params.flag = FLAG_LANGUAGE.flag_language;
break;
case MORE_MENU.Custom_Key:
TargetComponent = CustomKeyPage;
params.flag = FLAG_LANGUAGE.flag_key;
break;
case MORE_MENU.Remove_Key:
TargetComponent = CustomKeyPage;
params.flag = FLAG_LANGUAGE.flag_key;
params.isRemoveKey = true;
break;
case MORE_MENU.Sort_Language:
TargetComponent = SortKeyPage;
params.flag = FLAG_LANGUAGE.flag_language;
break;
case MORE_MENU.Sort_Key:
TargetComponent = SortKeyPage;
params.flag = FLAG_LANGUAGE.flag_key;
break;
case MORE_MENU.Custom_Theme:
this.setState({customThemeViewVisible: true});
break;
case MORE_MENU.About_Author:
TargetComponent = AboutMePage;
break;
case MORE_MENU.About:
TargetComponent = AboutPage;
break;
case '更新':
// this.update();
break;
}
if (TargetComponent) {
this.props.navigator.push({
component: TargetComponent,
params: params,
});
}
}
getItem(tag, icon, text) {
return ViewUtils.getSettingItem(() => this.onClick(tag), icon, text, this.state.theme.styles.tabBarSelectedIcon, null);
}
renderCustomThemeView() {
return (
<CustomTheme visible={this.state.customThemeViewVisible}
{...this.props}
onClose={() => {
this.setState({
customThemeViewVisible: false,
})
}}/>
)
}
render() {
let statusBar = {
backgroundColor:this.state.theme.themeColor
};
let navigatorBar = <NavigationBar title={'我的'}
statusBar={statusBar}
style={this.state.theme.styles.navBar}/>;
return <View style={GlobalStyles.root_container}>
{navigatorBar}
<ScrollView>
{/*最热管理*/}
<TouchableHighlight onPress={() => this.onClick(MORE_MENU.About)}>
<View style={[styles.item, {height: 90}]}>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Image source={require('../../../res/images/ic_trending.png')}
style={[{width: 40, height: 40, marginRight: 10}, this.state.theme.styles.tabBarSelectedIcon]}/>
<Text>Github Popular</Text>
</View>
<Image style={[{marginRight: 10, height: 22, width: 22,}, this.state.theme.styles.tabBarSelectedIcon]}
source={require('../../../res/images/ic_tiaozhuan.png')}/>
</View>
</TouchableHighlight>
<View style={GlobalStyles.line}/>
{/*趋势管理*/}
<Text style={styles.groupTitle}>趋势管理</Text>
<View style={GlobalStyles.line}/>
{/*自定义语言*/}
{this.getItem(MORE_MENU.Custom_Language, require('./img/ic_custom_language.png'), '自定义语言')}
<View style={GlobalStyles.line}/>
{/*语言排序*/}
{this.getItem(MORE_MENU.Sort_Language, require('./img/ic_sort.png'), '语言排序')}
<View style={GlobalStyles.line}/>
{/*标签管理*/}
<Text style={styles.groupTitle}>标签管理</Text>
<View style={GlobalStyles.line}/>
{/*自定义标签*/}
{this.getItem(MORE_MENU.Custom_Key, require('./img/ic_custom_language.png'), '自定义标签')}
<View style={GlobalStyles.line}/>
{/*标签排序*/}
{this.getItem(MORE_MENU.Sort_Key, require('./img/ic_swap_vert.png'), '标签排序')}
<View style={GlobalStyles.line}/>
{/*标签移除*/}
{this.getItem(MORE_MENU.Remove_Key, require('./img/ic_remove.png'), '标签移除')}
<View style={GlobalStyles.line}/>
{/*设置*/}
<Text style={styles.groupTitle}>设置</Text>
<View style={GlobalStyles.line}/>
{this.getItem(MORE_MENU.Custom_Theme, require('./img/ic_custom_theme.png'), '自定义主题')}
<View style={GlobalStyles.line}/>
{this.getItem(MORE_MENU.About_Author, require('./img/ic_insert_emoticon.png'), '关于作者')}
<View style={GlobalStyles.line}/>
</ScrollView>
{this.renderCustomThemeView()}
</View>
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
item: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 10,
height: 60,
backgroundColor: 'white',
},
groupTitle: {
marginLeft: 10,
marginTop: 10,
marginBottom: 5,
fontSize: 12,
color: 'gray',
}
});
//js/pages/My/CustomTheme.js
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ScrollView,
TouchableHighlight,
Image,
Platform,
DeviceEventEmitter,
Modal,
} from 'react-native';
import GlobalStyles from "../../../res/styles/GlobalStyles"
import ThemeFactory,{ThemeFlags} from "../../../res/styles/ThemeFactory"
import ThemeDao from "../../expand/dao/ThemeDao";
import {ACTION_HOME} from "../HomePage";
export default class CustomTheme extends Component {
constructor(props) {
super(props);
this.themeDao = new ThemeDao();
}
onSelectTheme(themeKey){
this.props.onClose();
this.themeDao.save(ThemeFlags[themeKey]);
DeviceEventEmitter.emit('ACTION_BASE',ACTION_HOME.A_THEME,ThemeFactory.createTheme(
ThemeFlags[themeKey]
))
}
getThemeItem(themeKey){
return <TouchableHighlight underlayColor={'white'} onPress={()=>this.onSelectTheme(themeKey)} style={{flex: 1}}>
<View style={[{backgroundColor: ThemeFlags[themeKey]},styles.themeItem]}>
<Text style={styles.themeText}>{themeKey}</Text>
</View>
</TouchableHighlight>
}
/**
* 创建主题列表
* @returns {Array}
*/
renderThemeItems(){
const views = [];
for (let i=0,keys=Object.keys(ThemeFlags),l=keys.length;i<l;i+=3){
const key1 = keys[i], key2 = keys[i + 1], key3 = keys[i + 2];
views.push(
<View keys={i} style={{flexDirection: 'row'}}>
{this.getThemeItem(key1)}
{this.getThemeItem(key2)}
{this.getThemeItem(key3)}
</View>
)
}
return views;
}
renderContentView(){
return <Modal animationType={"slide"}
transparent={true}
visible={this.props.visible}
onRequestClose={() => {this.props.onClose()}}>
<View style={styles.modalContainer}>
<ScrollView>
{this.renderThemeItems()}
</ScrollView>
</View>
</Modal>
}
render() {
let view = this.props.visible?<View style={GlobalStyles.root_container}>
{this.renderContentView()}
</View>:null;
return view;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
modalContainer:{
flex:1,
margin: 10,
marginTop: Platform.OS==='ios'?20:10,
backgroundColor:'white',
borderRadius: 3,
shadowColor: 'gray',
shadowOffset: {width: 2,height: 2},
shadowOpacity: 0.5,
shadowRadius: 2,
padding: 3
},
themeItem:{
flex:1,
height: 120,
margin: 3,
padding:3,
borderRadius:2,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
themeText:{
color:'white',
fontWeight: '500',
fontSize:16,
}
});
by最后,如果你看代码看的想睡觉,那么它是提醒你,换一个项目看看~
没有看代码看睡着的人,只有不够有吸引力的代码
作者:jser_dimple
-------------------------------------------
个性签名:一个人在年轻的时候浪费自己的才华与天赋是一件非常可惜的事情
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏5毛买辣条行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
微信
支付宝