Skip to content

feat: Paged events screen #736

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

Merged
merged 6 commits into from
Mar 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions __tests__/tests/reducers/auth.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
GET_AUTH_ORGS,
GET_AUTH_STAR_COUNT,
GET_AUTH_USER,
GET_EVENTS,
LOGIN,
LOGOUT,
} from 'auth/auth.type';
Expand Down Expand Up @@ -234,49 +233,6 @@ describe('Auth Reducer', () => {
expect(authReducer(initialState, action)).toEqual(expectedState);
});

/**
* Get events for user.
*/
it('should set isPendingEvents: true when GET_AUTH_ORGS.PENDING action is dispatched', () => {
const expectedState = {
...initialState,
isPendingEvents: true,
};
const action = {
type: GET_EVENTS.PENDING,
};

expect(authReducer(initialState, action)).toEqual(expectedState);
});

it('should set events from payload of GET_AUTH_ORGS.SUCCESS action and set isPendingEvents: false', () => {
const expectedState = {
...initialState,
isPendingEvents: false,
events,
};
const action = {
type: GET_EVENTS.SUCCESS,
payload: events,
};

expect(authReducer(initialState, action)).toEqual(expectedState);
});

it('should set an error from payload of GET_AUTH_ORGS.ERROR action and set isPendingEvents: false', () => {
const expectedState = {
...initialState,
isPendingEvents: false,
error: authError,
};
const action = {
type: GET_EVENTS.ERROR,
payload: authError,
};

expect(authReducer(initialState, action)).toEqual(expectedState);
});

/**
* Set locale.
*/
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@
"enzyme": "^3.0.0",
"enzyme-adapter-react-16": "^1.0.0",
"fuzzysort": "^1.0.1",
"lodash.uniqby": "^4.7.0",
"lodash": "^4.17.5",
"lowlight": "^1.5.0",
"md5": "^2.2.1",
"node-emoji": "^1.7.0",
"normalizr": "^3.2.4",
"opencollective": "^1.0.3",
"parse-diff": "^0.4.0",
"query-string": "^5.0.1",
Expand Down
3 changes: 3 additions & 0 deletions root.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { organizationReducer } from 'organization';
import { issueReducer } from 'issue';
import { searchReducer } from 'search';
import { notificationsReducer } from 'notifications';
import { entities, pagination } from 'api/reducers';

export const rootReducer = combineReducers({
auth: authReducer,
Expand All @@ -15,4 +16,6 @@ export const rootReducer = combineReducers({
issue: issueReducer,
search: searchReducer,
notifications: notificationsReducer,
entities,
pagination,
});
5 changes: 5 additions & 0 deletions src/api/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createPaginationActionSet } from 'utils';

export const ACTIVITY_GET_EVENTS_RECEIVED = createPaginationActionSet(
'ACTIVITY_GET_EVENTS_RECEIVED'
);
145 changes: 145 additions & 0 deletions src/api/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import Schemas from './schemas';

export class Client {
API_ROOT = 'https://api.github.com/';

/**
* Enum for HTTP methods.
*
* @enum {string}
*/
Method = {
GET: 'GET',
HEAD: 'HEAD',
PUT: 'PUT',
DELETE: 'DELETE',
PATCH: 'PATCH',
POST: 'POST',
};

authHeaders = {};

call = async (
url,
params = {},
{ method = this.Method.GET, body = {}, headers = {} } = {}
) => {
let finalUrl;

if (params.url) {
// a different url was provided, use it instead (paginated)
finalUrl = params.url;
} else {
finalUrl = url;
// add explicitely specified parameters
if (params.per_page) {
finalUrl = `${finalUrl}${finalUrl.includes('?') ? '&' : '?'}per_page=${
params.per_page
}`;
}
}

if (!finalUrl.includes(this.API_ROOT)) {
finalUrl = `${this.API_ROOT}${finalUrl}`;
}

const parameters = {
method,
headers: {
'Cache-Control': 'no-cache',
...this.authHeaders,
...headers,
},
};

const withBody = [this.Method.PUT, this.Method.PATCH, this.Method.POST];

if (withBody.indexOf(method) !== -1) {
parameters.body = JSON.stringify(body);
if (method === this.Method.PUT) {
parameters.headers['Content-Length'] = 0;
}
}

return fetch(finalUrl, parameters)
.then(response => response)
.catch(error => error);
};

/* eslint-disable no-unused-vars */

/**
* Sets the authorization headers given an access token.
*
* @abstract
* @param {string} token The oAuth access token
*/
setAuthHeaders = token => {
this.authHeaders = { Authorization: `token ${token}` };
};

/**
* Counts the entities available by analysing the Response object
*
* @async
* @param {Response} response
*/
getCount = async response => {
if (!response.ok) {
return 0;
}

let linkHeader = response.headers.get('Link');

if (linkHeader !== null) {
linkHeader = linkHeader.match(/page=(\d)+/g).pop();

return linkHeader.split('=').pop();
}

return response
.json()
.then(data => data.length)
.catch(() => {
// TODO: Properly handle the error and show it if needed.
return 0;
});
};

getNextPageUrl = response => {
const { headers } = response;
const link = headers.get('link');
const nextLink = link
? link.split(',').find(s => s.indexOf('rel="next"') > -1)
: null;

if (!nextLink) {
return null;
}

return nextLink
.trim()
.split(';')[0]
.slice(1, -1);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can simplify this method to something like:

 getNextPageUrl = response => {
    const { headers } = response;
    const link = headers.get('link');
    const nextLink = link ? link.split(',').find(s => s.indexOf('rel="next"') > -1) : null;

    if (!nextLink) {
      return;
    }

    return nextLink
      .trim()
      .split(';')[0]
      .slice(1, -1);
  };

Not picky however 😁 The way it currently is is also fine

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!


/**
* The activity endpoint
*/
activity = {
/**
* Gets received events
*
* @param {string} userId
*/
getEventsReceived: async (userId, params) => {
return this.call(`users/${userId}/received_events`, params).then(
response => ({
response,
nextPageUrl: this.getNextPageUrl(response),
schema: Schemas.EVENT_ARRAY,
})
);
},
};
}
8 changes: 5 additions & 3 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { createDispatchProxy } from './proxies';
import { Client } from './client';

// These keys are for development purposes and do not represent the actual application keys.
// Feel free to use them or use a new set of keys by creating an OAuth application of your own.
// https://github.com/settings/applications/new
export const CLIENT_ID = '87c7f05700c052937cfb';
export const CLIENT_SECRET = '3a70aee4d5e26c457720a31c3efe2f9062a4997a';

export const RestClient = createDispatchProxy(Client);

const ACCEPT = {
DIFF: 'application/vnd.github.v3.diff+json',
FULL: 'application/vnd.github.v3.full+json',
Expand Down Expand Up @@ -205,9 +210,6 @@ export const fetchUser = (user, accessToken) =>
export const fetchUserOrgs = (user, accessToken) =>
v3.getJson(`/users/${user}/orgs`, accessToken);

export const fetchUserEvents = (user, accessToken) =>
v3.getJson(`/users/${user}/received_events?per_page=100`, accessToken);

export const fetchReadMe = (user, repository, accessToken) =>
v3.getHtml(`/repos/${user}/${repository}/readme?ref=master`, accessToken);

Expand Down
Loading