Skip to content

Commit

Permalink
Add ViewPager for home list
Browse files Browse the repository at this point in the history
  • Loading branch information
race604 committed Oct 8, 2015
1 parent 405251b commit b491f8b
Show file tree
Hide file tree
Showing 5 changed files with 605 additions and 13 deletions.
54 changes: 42 additions & 12 deletions DataRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var {
} = React;

var API_COVER_URL = "http://news-at.zhihu.com/api/4/start-image/1080*1776";
var API_LATEST_URL = 'http://news.at.zhihu.com/api/4/news/latest';
var API_LATEST_URL = 'http://news-at.zhihu.com/api/4/news/latest';
var API_HOME_URL = 'http://news.at.zhihu.com/api/4/news/before/';
var API_THEME_URL = 'http://news-at.zhihu.com/api/4/theme/';
var API_THEMES_URL = 'http://news-at.zhihu.com/api/4/themes';
Expand All @@ -16,6 +16,7 @@ var KEY_COVER = '@Cover';
var KEY_THEMES = '@Themes:';
var KEY_HOME_LIST = '@HomeList:';
var KEY_THEME_LIST = '@ThemeList:';
var KEY_THEME_TOPDATA = '@ThemeTop:';

function parseDateFromYYYYMMdd(str) {
if (!str) return new Date();
Expand Down Expand Up @@ -85,18 +86,24 @@ DataRepository.prototype.updateCover = function() {
DataRepository.prototype.fetchStories = function(date?: Date,
callback?: ?(error: ?Error, result: ?Object) => void
) {
var reqUrl;
var topData = null;
if (!date) {
date = new Date();
};
reqUrl = API_LATEST_URL;
topData = this._safeStorage(KEY_THEME_TOPDATA);
} else {
var beforeDate = new Date(date);
beforeDate.setDate(date.getDate() + 1);
reqUrl = API_HOME_URL + beforeDate.yyyymmdd();
}

var localStorage = this._safeStorage(KEY_HOME_LIST + date.yyyymmdd());

var beforeDate = new Date(date);
beforeDate.setDate(date.getDate() + 1);
var networking = this._safeFetch(API_HOME_URL + beforeDate.yyyymmdd());
var networking = this._safeFetch(reqUrl);

var merged = new Promise((resolve, reject) => {
Promise.all([localStorage, networking])
Promise.all([localStorage, networking, topData])
.then((values) => {
var error, result;
result = this._mergeReadState(values[0], values[1]);
Expand All @@ -107,14 +114,19 @@ DataRepository.prototype.fetchStories = function(date?: Date,
if (error) {
reject(error);
} else {
if (values[1] && values[1].top_stories) {
result.topData = values[1].top_stories;
} else {
result.topData = values[2];
}
resolve(result);
}
});
});
return merged;
};

DataRepository.prototype.fetchThemeStories = function(themeId: Number, lastID?: string,
DataRepository.prototype.fetchThemeStories = function(themeId: number, lastID?: string,
callback?: ?(error: ?Error, result: ?Object) => void
) {
// Home story list
Expand All @@ -123,10 +135,7 @@ DataRepository.prototype.fetchThemeStories = function(themeId: Number, lastID?:
if (lastID) {
date = parseDateFromYYYYMMdd(lastID);
date.setDate(date.getDate() - 1);
} else {
date = new Date();
}

return this.fetchStories(date, callback);
}

Expand All @@ -135,14 +144,17 @@ DataRepository.prototype.fetchThemeStories = function(themeId: Number, lastID?:
var localStorage = isRefresh ? this._safeStorage(KEY_THEME_LIST + themeId) : null;

var reqUrl = API_THEME_URL + themeId;
var topData = null;
if (lastID) {
reqUrl += '/before/' + lastID;
} else {
topData = this._safeStorage(KEY_THEME_TOPDATA + themeId);
}

var networking = this._safeFetch(reqUrl);

var merged = new Promise((resolve, reject) => {
Promise.all([localStorage, networking])
Promise.all([localStorage, networking, topData])
.then((values) => {
var error, result;
result = this._mergeReadState(values[0], values[1]);
Expand All @@ -153,6 +165,16 @@ DataRepository.prototype.fetchThemeStories = function(themeId: Number, lastID?:
if (error) {
reject(error);
} else {
var topDataRet;
if (values[1] && values[1].editors) {
topDataRet = {};
topDataRet.description = values[1].description;
topDataRet.background = values[1].background;
topDataRet.editors = values[1].editors;
} else {
topDataRet = values[2];
}
result.topData = topDataRet;
resolve(result);
}
});
Expand All @@ -161,7 +183,7 @@ DataRepository.prototype.fetchThemeStories = function(themeId: Number, lastID?:
return merged;
};

DataRepository.prototype.saveStories = function(themeList: object,
DataRepository.prototype.saveStories = function(themeList: object, topData: object,
callback?: ?(error: ?Error, result: ?Object) => void
) {
var homeList = themeList[0];
Expand All @@ -182,6 +204,14 @@ DataRepository.prototype.saveStories = function(themeList: object,
}
}
}

for (var theme in topData) {
if (topData.hasOwnProperty(theme)) {
//console.log(theme, topData[key]);
keyValuePairs.push([KEY_THEME_TOPDATA + theme, JSON.stringify(topData[theme])]);
}
}

AsyncStorage.multiSet(keyValuePairs, callback);
};

Expand Down
102 changes: 102 additions & 0 deletions DefaultViewPageIndicator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use strict';

var React = require('react-native');
var {
Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View,
Animated,
} = React;

var deviceWidth = Dimensions.get('window').width;
var DOT_SIZE = 6;
var DOT_SAPCE = 3;

var styles = StyleSheet.create({
tab: {
alignItems: 'center',
},

tabs: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},

dot: {
width: DOT_SIZE,
height: DOT_SIZE,
borderRadius: DOT_SIZE / 2,
backgroundColor: 'rgba(100,100,100,0.5)',
marginLeft: DOT_SAPCE,
marginRight: DOT_SAPCE,
},

curDot: {
position: 'absolute',
width: DOT_SIZE,
height: DOT_SIZE,
borderRadius: DOT_SIZE / 2,
backgroundColor: 'rgba(250,250,250,0.8)',
margin: DOT_SAPCE,
bottom: 0,
},
});

var DefaultViewPageIndicator = React.createClass({
propTypes: {
goToPage: React.PropTypes.func,
activePage: React.PropTypes.number,
pageCount: React.PropTypes.number
},

getInitialState() {
return {
viewWidth: 0,
};
},

renderIndicator(page) {
//var isTabActive = this.props.activePage === page;
return (
<TouchableOpacity style={styles.tab} key={'idc_' + page} onPress={() => this.props.goToPage(page)}>
<View style={styles.dot} />
</TouchableOpacity>
);
},

render() {
var pageCount = this.props.pageCount;
var itemWidth = DOT_SIZE + (DOT_SAPCE * 2);
var offset = (this.state.viewWidth - itemWidth * pageCount) / 2 + itemWidth * this.props.activePage;

var left = offset; /*this.state.offsetX.interpolate({
inputRange: [0, 1], outputRange: [0, offset]
});*/

var indicators = [];
for (var i = 0; i < pageCount; i++) {
indicators.push(this.renderIndicator(i))
}

return (
<View style={styles.tabs}
onLayout={(event) => {
var viewWidth = event.nativeEvent.layout.width;
if (!viewWidth || this.state.viewWidth === viewWidth) {
return;
}
this.setState({
viewWidth: viewWidth,
});
}}>
{indicators}
<Animated.View style={[styles.curDot, {left}]} />
</View>
);
},
});

module.exports = DefaultViewPageIndicator;
71 changes: 70 additions & 1 deletion ListScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var StoryItem = require('./StoryItem');
var ThemesList = require('./ThemesList');
var DataRepository = require('./DataRepository');
var SwipeRefreshLayoutAndroid = require('./SwipeRereshLayout');
var ViewPager = require('./ViewPager');

var API_LATEST_URL = 'http://news.at.zhihu.com/api/4/news/latest';
var API_HISTORY_URL = 'http://news.at.zhihu.com/api/4/news/before/';
Expand All @@ -44,6 +45,7 @@ var repository = new DataRepository();

var dataCache = {
dataForTheme: {},
topDataForTheme: {},
sectionsForTheme: {},
lastID: {},
};
Expand All @@ -68,18 +70,23 @@ var ListScreen = React.createClass({
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
});

var headerDataSource = new ViewPager.DataSource({
pageHasChanged: (p1, p2) => p1 !== p2,
});

return {
isLoading: false,
isLoadingTail: false,
theme: null,
dataSource: dataSource,
headerDataSource: headerDataSource,
};
},
componentWillMount: function() {
BackAndroid.addEventListener('hardwareBackPress', this._handleBackButtonPress);
},
componentWillUnmount: function() {
repository.saveStories(dataCache.dataForTheme);
repository.saveStories(dataCache.dataForTheme, dataCache.topDataForTheme);
},
_handleBackButtonPress: function() {
if (this.state.theme) {
Expand All @@ -101,6 +108,7 @@ var ListScreen = React.createClass({
dataBlob = isInTheme ? [] : {};
}
var sectionIDs = dataCache.sectionsForTheme[themeId];
var topData = dataCache.topDataForTheme[themeId];

this.setState({
isLoading: isRefresh,
Expand All @@ -113,6 +121,7 @@ var ListScreen = React.createClass({
.then((responseData) => {
var newLastID;
var dataSouce;
var headerDataSource = this.state.headerDataSource;
if (!isInTheme) {
newLastID = responseData.date;
var newDataBlob = {};
Expand All @@ -131,7 +140,14 @@ var ListScreen = React.createClass({

dataBlob = newDataBlob;
sectionIDs = newSectionIDs;
console.log(responseData);
if (isRefresh && responseData.topData) {
topData = responseData.topData;
headerDataSource = headerDataSource.cloneWithPages(topData.slice())
}

dataSouce = this.state.dataSource.cloneWithRowsAndSections(newDataBlob, newSectionIDs, null);

} else {
var length = responseData.stories.length;
if (length > 0) {
Expand Down Expand Up @@ -159,6 +175,7 @@ var ListScreen = React.createClass({
isLoadingTail: (isRefresh ? this.state.isLoadingTail : false),
theme: this.state.theme,
dataSource: dataSouce,
headerDataSource: headerDataSource,
});

this.swipeRefreshLayout && this.swipeRefreshLayout.finishRefresh();
Expand All @@ -175,6 +192,36 @@ var ListScreen = React.createClass({
})
.done();
},
_renderPage: function(
story: Object,
pageID: number | string,) {
return (
<Image
source={{uri: story.image}}
style={styles.headerItem} >
<View style={styles.headerTitleContainer}>
<Text style={styles.headerTitle}
numberOfLines={2}>
{story.title}
</Text>
</View>
</Image>
)
},
_renderHeader: function() {
if (this.state.theme) {

} else {
return (
<View style={{flex: 1, height: 200}}>
<ViewPager
dataSource={this.state.headerDataSource}
style={styles.listHeader}
renderPage={this._renderPage}/>
</View>
);
}
},
getSectionTitle: function(str) {
var date = parseDateFromYYYYMMdd(str);
if (date.toDateString() == new Date().toDateString()) {
Expand Down Expand Up @@ -273,6 +320,7 @@ var ListScreen = React.createClass({
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps={true}
showsVerticalScrollIndicator={false}
renderHeader={this._renderHeader}
/>;
var title = this.state.theme ? this.state.theme.name : '首页';
return (
Expand Down Expand Up @@ -330,6 +378,27 @@ var styles = StyleSheet.create({
color: '#888888',
margin: 10,
marginLeft: 16,
},
headerPager: {
height: 200,
},
headerItem: {
flex: 1,
height: 200,
flexDirection: 'row',
},
headerTitleContainer: {
flex: 1,
alignSelf: 'flex-end',
padding: 10,
backgroundColor: 'rgba(0,0,0,0.2)',
},
headerTitle: {
flex: 1,
fontSize: 18,
fontWeight: '500',
color: 'white',
marginBottom: 10,
}
});

Expand Down
Loading

0 comments on commit b491f8b

Please sign in to comment.