-
Notifications
You must be signed in to change notification settings - Fork 783
feat: Make the starred repositories list paged #750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fdabf02
0e4f213
60ca6ba
e66e4a9
4a98a8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,5 +141,12 @@ export class Client { | |
}) | ||
); | ||
}, | ||
getStarredReposForUser: async (userId, params) => { | ||
return this.call(`users/${userId}/starred`, params).then(response => ({ | ||
response, | ||
nextPageUrl: this.getNextPageUrl(response), | ||
schema: Schemas.REPO_ARRAY, | ||
})); | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Second, we implement our new API call: The schema key tells which kind of objects the call to GitHub will be returning: An array of repositories. PS: I'm following @oktokit/rest for the choice of namespace/method names. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Outside the scope of this PR, but curious Where is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In our FlatList When the proxy sees this parameter, it overrides the GitHub URL that will be called with |
||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,4 +78,7 @@ const paginate = types => { | |
// Updates the pagination data for different actions. | ||
export const pagination = combineReducers({ | ||
ACTIVITY_GET_EVENTS_RECEIVED: paginate(Actions.ACTIVITY_GET_EVENTS_RECEIVED), | ||
ACTIVITY_GET_STARRED_REPOS_FOR_USER: paginate( | ||
Actions.ACTIVITY_GET_STARRED_REPOS_FOR_USER | ||
), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We tell our pagination reducer that it should take care of this action set. |
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import { eventSchema } from './events'; | ||
import { repoSchema } from './repos'; | ||
|
||
export default { | ||
EVENT: eventSchema, | ||
EVENT_ARRAY: [eventSchema], | ||
REPO: repoSchema, | ||
REPO_ARRAY: [repoSchema], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { schema } from 'normalizr'; | ||
|
||
export const repoSchema = new schema.Entity( | ||
'repos', | ||
{}, | ||
{ | ||
idAttribute: repo => repo.full_name.toLowerCase(), | ||
} | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,85 @@ | ||
import React, { Component } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { bindActionCreators } from 'redux'; | ||
import { FlatList } from 'react-native'; | ||
|
||
import { getStarredRepositories } from 'user'; | ||
import { FlatList, View, ActivityIndicator } from 'react-native'; | ||
|
||
import { RestClient } from 'api'; | ||
import { | ||
ViewContainer, | ||
RepositoryListItem, | ||
LoadingRepositoryListItem, | ||
} from 'components'; | ||
|
||
const mapStateToProps = state => ({ | ||
user: state.user.user, | ||
starredRepositories: state.user.starredRepositories, | ||
isPendingStarredRepositories: state.user.isPendingStarredRepositories, | ||
}); | ||
const mapStateToProps = (state, ownProps) => { | ||
const { | ||
auth: { user }, | ||
pagination: { ACTIVITY_GET_STARRED_REPOS_FOR_USER }, | ||
entities: { repos }, | ||
} = state; | ||
|
||
const userId = ownProps.navigation.state.params.user.login; | ||
|
||
const starredReposPagination = ACTIVITY_GET_STARRED_REPOS_FOR_USER[ | ||
userId | ||
] || { | ||
ids: [], | ||
isFetching: true, | ||
}; | ||
const starredRepos = starredReposPagination.ids.map(id => repos[id]); | ||
|
||
return { | ||
user, | ||
starredReposPagination, | ||
starredRepos, | ||
userId, | ||
}; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mapStateToProps now create two props:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
const mapDispatchToProps = dispatch => | ||
bindActionCreators({ getStarredRepositories }, dispatch); | ||
const mapDispatchToProps = { | ||
getStarredReposForUser: RestClient.activity.getStarredReposForUser, | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this.props.getStarredReposForUser() is mapped to our API call. |
||
|
||
class StarredRepositoryList extends Component { | ||
props: { | ||
user: Object, | ||
starredRepositories: Array, | ||
userId: String, | ||
starredRepos: Array, | ||
starredReposPagination: Object, | ||
getStarredReposForUser: Function, | ||
navigation: Object, | ||
isPendingStarredRepositories: boolean, | ||
getStarredRepositories: Function, | ||
}; | ||
|
||
componentWillMount() { | ||
const user = this.props.navigation.state.params.user; | ||
const { getStarredReposForUser, userId } = this.props; | ||
|
||
this.props.getStarredRepositories(user); | ||
getStarredReposForUser(userId); | ||
} | ||
|
||
renderFooter = () => { | ||
if (this.props.starredReposPagination.nextPageUrl === null) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<View | ||
style={{ | ||
paddingVertical: 20, | ||
}} | ||
> | ||
<ActivityIndicator animating size="large" /> | ||
</View> | ||
); | ||
}; | ||
|
||
render() { | ||
const { | ||
starredRepositories, | ||
starredReposPagination, | ||
starredRepos, | ||
userId, | ||
navigation, | ||
isPendingStarredRepositories, | ||
} = this.props; | ||
|
||
const repoCount = navigation.state.params.repoCount; | ||
const isPendingStarredRepositories = | ||
starredRepos.length === 0 && starredReposPagination.isFetching; | ||
|
||
return ( | ||
<ViewContainer> | ||
|
@@ -52,7 +89,18 @@ class StarredRepositoryList extends Component { | |
)} | ||
{!isPendingStarredRepositories && ( | ||
<FlatList | ||
data={starredRepositories} | ||
data={starredRepos} | ||
onRefresh={() => | ||
this.props.getStarredReposForUser(userId, { | ||
forceRefresh: true, | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remember how our action set contains the 3 usual pending,success,error and an additional 'reset' thingy? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was just about to ask about why we have |
||
refreshing={!starredRepos && starredReposPagination.isFetching} | ||
onEndReached={() => | ||
this.props.getStarredReposForUser(userId, { loadMore: true }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
onEndReachedThreshold={0.5} | ||
ListFooterComponent={this.renderFooter} | ||
renderItem={({ item }) => ( | ||
<RepositoryListItem repository={item} navigation={navigation} /> | ||
)} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,6 @@ import { | |
GET_IS_FOLLOWING, | ||
GET_IS_FOLLOWER, | ||
GET_REPOSITORIES, | ||
GET_STARRED_REPOSITORIES, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here, we removed our old boring actions, types, reducers. So long! |
||
GET_FOLLOWERS, | ||
GET_FOLLOWING, | ||
SEARCH_USER_REPOS, | ||
|
@@ -202,30 +201,6 @@ export const getRepositories = user => { | |
}; | ||
}; | ||
|
||
export const getStarredRepositories = user => { | ||
return (dispatch, getState) => { | ||
const url = `/users/${user.login}/starred?per_page=50`; | ||
const accessToken = getState().auth.accessToken; | ||
|
||
dispatch({ type: GET_STARRED_REPOSITORIES.PENDING }); | ||
|
||
v3 | ||
.getJson(url, accessToken) | ||
.then(data => { | ||
dispatch({ | ||
type: GET_STARRED_REPOSITORIES.SUCCESS, | ||
payload: data, | ||
}); | ||
}) | ||
.catch(error => { | ||
dispatch({ | ||
type: GET_STARRED_REPOSITORIES.ERROR, | ||
payload: error, | ||
}); | ||
}); | ||
}; | ||
}; | ||
|
||
export const getFollowing = user => { | ||
return (dispatch, getState) => { | ||
const accessToken = getState().auth.accessToken; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, we create our new actions. A "pagination action" in a object composed of the 3 usual sub-actions (pending, success, error) and an additional one:
reset
.