-
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
Conversation
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.
Added walk through.
@@ -3,3 +3,6 @@ import { createPaginationActionSet } from 'utils'; | |||
export const ACTIVITY_GET_EVENTS_RECEIVED = createPaginationActionSet( | |||
'ACTIVITY_GET_EVENTS_RECEIVED' | |||
); | |||
export const ACTIVITY_GET_STARRED_REPOS_FOR_USER = createPaginationActionSet( | |||
'ACTIVITY_GET_STARRED_REPOS_FOR_USER' | |||
); |
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
.
nextPageUrl: this.getNextPageUrl(response), | ||
schema: Schemas.REPO_ARRAY, | ||
})); | ||
}, |
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.
Second, we implement our new API call: client.activity.getStarredReposForUser(userId)
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Outside the scope of this PR, but curious
Where is nextPageUrl
being used? Is it part of the pagination flow within proxy to define a nextPageUrl
property so it knows how to continue moving past pages?
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.
In our FlatList onEndReached
, we call client.activity.getStarredReposForUser(login, { loadMore: true})
When the proxy sees this parameter, it overrides the GitHub URL that will be called with nextPageUrl
: https://github.com/gitpoint/git-point/blob/master/src/api/proxies/create-dispatch-proxy.js#L54
@@ -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 comment
The 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.
src/api/schemas/repos.js
Outdated
{ | ||
idAttribute: repo => repo.full_name, | ||
} | ||
); |
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.
This is where we define our Repository schema for Normalizr.
For now, we simply tell it to use the repository full name as an identifier.
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.
Above is everything that is required to get going. All the logic will happen under the hood:
When client.activity.getStarredReposForUser('machour')
is called, the dispatch proxy does this:
- Analyzes the call and extract the following information:
- namespace: activity
- method: getStarredReposForUser
- parameters: ['machour']
-
Automatically guess the action set to be used from 'namespace' and 'method' : ACTIVITY_GET_STARRED_REPOS_FOR_USER !
-
Checks if this is a paginated call, by checking if STORE.pagination.ACTIVITY_GET_STARRED_REPOS_FOR_USER exists. It does since we added it to the paginate() reducer.
-
Checks if we already have data inside STORE.pagination.ACTIVITY_GET_STARRED_REPOS_FOR_USER['machour']
'machour' is a '-' separated string of our parameters.
So client.namespace.method("foo", "bar", 42)
would have checked inside:
STORE.pagination.NAMESPACE_METHOD['foo-bar-42']
-
No data, "ACTIVITY_GET_STARRED_REPOS_FOR_USER.pending" is dispatched, the API call is performed, and the returned json is treated as an array of repositories (as we said in the
schema
key) -
After "success" is dispatched, we end up with this in our store:
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.
Ah this makes so much sense, and it makes things so so clean by abstracting it all away 👏 👏
starredRepos, | ||
userId, | ||
}; | ||
}; |
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.
mapStateToProps now create two props:
- starredReposPagination: STORE.pagination.ACTIVITY_GET_STARRED_REPOS_FOR_USER['machour'] or and empty pagination
- starredRepos: An array of repositories picked from STORE.entities.repos, filtered to contain only those whose id is present in my pagination.
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.
Is starredReposPagination
just an object that returns nextPageUrl
and an isFetching
bool?
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.
bindActionCreators({ getStarredRepositories }, dispatch); | ||
const mapDispatchToProps = { | ||
getStarredReposForUser: RestClient.activity.getStarredReposForUser, | ||
}; |
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.
this.props.getStarredReposForUser() is mapped to our API call.
this.props.getStarredReposForUser(userId, { | ||
forceRefresh: true, | ||
}) | ||
} |
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.
Remember how our action set contains the 3 usual pending,success,error and an additional 'reset' thingy?
'reset' is dispatched when 'forceRefresh' is set to true
, and it will simply wipe out STORE.pagination.ACTIVITY_GET_STARRED_REPOS_FOR_USER['machour']
, so that fresh information is refetched from the GitHub endpoint
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.
Was just about to ask about why we have reset
. You've thought this through mate :)
} | ||
refreshing={!starredRepos && starredReposPagination.isFetching} | ||
onEndReached={() => | ||
this.props.getStarredReposForUser(userId, { loadMore: true }) |
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.
loadMore
tells the proxy to use nextPageUrl
as endpoint for the next API call, instead of the original endpoint, and the new results are appended to the pagination.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
And here, we removed our old boring actions, types, reducers. So long!
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.
WOW this is so good. So so good.
The number of screens we have that literally follow the exact same logic and can be simplified and we cana easily have paginated logic throughout the app :)
Thanks a million. I've left a few comments just asking questions but feel free to merge this right in.
To fully digest how to use it, I'm going to incorporate this logic to a separate screen and use this PR as reference 👏
nextPageUrl: this.getNextPageUrl(response), | ||
schema: Schemas.REPO_ARRAY, | ||
})); | ||
}, |
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.
Outside the scope of this PR, but curious
Where is nextPageUrl
being used? Is it part of the pagination flow within proxy to define a nextPageUrl
property so it knows how to continue moving past pages?
src/api/schemas/repos.js
Outdated
{ | ||
idAttribute: repo => repo.full_name, | ||
} | ||
); |
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.
Ah this makes so much sense, and it makes things so so clean by abstracting it all away 👏 👏
this.props.getStarredReposForUser(userId, { | ||
forceRefresh: true, | ||
}) | ||
} |
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.
Was just about to ask about why we have reset
. You've thought this through mate :)
starredRepos, | ||
userId, | ||
}; | ||
}; |
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.
Is starredReposPagination
just an object that returns nextPageUrl
and an isFetching
bool?
Made a tiny change: the repo id (full name) is now stored in lower case. This make things more consistent internally and will save us a headache if for some reason, one endpoint respond with a lowercased name. |
Sadely, last change made an important comment outdated, reposting it: Here's all the logic that will happen under the hood: When
'machour' is a '-' separated string of our parameters. So
|
Description
In order to further explain the new API, this PR demonstrate how we turn a limited list of repositories (50), into a paged list displaying all repositories. I did that on the starred repositories list displayed on a user profile.
By chance, the file changes are well ordered, so I'll break down the migration steps in comments.
Once merged, we will have two screens using the new pagination API (events & starred repos), so next PR will be about a little refactoring:
<PaginatedFlatList />
component and using it in both screens.getPagination()
helper to reduce the amount code needed inmapStateToProps