Skip to content

Commit 89449ba

Browse files
Merge pull request #5 from zjuasmn/stats
add member stats history/distribution/activeChallenges
2 parents 1f91d41 + aaa8043 commit 89449ba

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

src/actions/members.js

+102
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { createActions } from 'redux-actions';
77
import { getService } from '../services/members';
88
import { getService as getUserService } from '../services/user';
9+
import { getService as getChallengesService } from '../services/challenges';
910

1011
/**
1112
* @static
@@ -104,6 +105,101 @@ async function getStatsDone(handle, uuid, tokenV3) {
104105
return { data, handle, uuid };
105106
}
106107

108+
/**
109+
* Payload creator for the action that inits the loading of member active challenges.
110+
* @param {String} handle
111+
* @param {String} uuid
112+
* @returns {Object} Payload
113+
*/
114+
async function getActiveChallengesInit(handle, uuid) {
115+
return { handle, uuid };
116+
}
117+
118+
/**
119+
* Payload creator for the action that loads the member active challenges.
120+
* @param {String} handle
121+
* @param {String} uuid
122+
* @param {String} tokenV3
123+
* @returns {Object} Payload
124+
*/
125+
async function getActiveChallengesDone(handle, uuid, tokenV3) {
126+
const filter = { status: 'ACTIVE' };
127+
const service = getChallengesService(tokenV3);
128+
/* TODO: Reuse `getAll` from `actions/challenge-listing`
129+
/* after it moved from `community-app` to here.
130+
*/
131+
function getAll(getter, page = 0, prev = null) {
132+
const PAGE_SIZE = 50;
133+
return getter({
134+
limit: PAGE_SIZE,
135+
offset: page * PAGE_SIZE,
136+
}).then(({ challenges: chunk }) => {
137+
if (!chunk.length) return prev || [];
138+
return getAll(getter, 1 + page, prev ? prev.concat(chunk) : chunk);
139+
});
140+
}
141+
const calls = [
142+
getAll(params =>
143+
service.getUserChallenges(handle, filter, params)),
144+
getAll(params =>
145+
service.getUserMarathonMatches(handle, filter, params)),
146+
];
147+
148+
const [challenges] = await Promise.all(calls);
149+
150+
return { handle, challenges, uuid };
151+
}
152+
153+
/**
154+
* @static
155+
* @desc Create an action that signals beginning of member stats distribution history.
156+
* @param {String} handle Member handle.
157+
* @param {String} uuid Operation UUID.
158+
* @return {Action}
159+
*/
160+
async function getStatsHistoryInit(handle, uuid) {
161+
return { handle, uuid };
162+
}
163+
164+
/**
165+
* @static
166+
* @desc Create an action that loads the member stats history.
167+
* @param {String} handle Member handle.
168+
* @param {String} uuid Operation UUID.
169+
* @param {String} tokenV3 v3 auth token.
170+
* @return {Action}
171+
*/
172+
async function getStatsHistoryDone(handle, uuid, tokenV3) {
173+
const data = await getService(tokenV3).getStatsHistory(handle);
174+
return { data, handle, uuid };
175+
}
176+
177+
/**
178+
* @static
179+
* @desc Create an action that signals beginning of member stats distribution loading.
180+
* @param {String} handle Member handle.
181+
* @param {String} uuid Operation UUID.
182+
* @return {Action}
183+
*/
184+
async function getStatsDistributionInit(handle, uuid) {
185+
return { handle, uuid };
186+
}
187+
188+
/**
189+
* @static
190+
* @desc Create an action that loads the member stats distribution.
191+
* @param {String} handle Member handle.
192+
* @param {String} track Main track name.
193+
* @param {String} subTrack Subtrack name.
194+
* @param {String} uuid Operation UUID.
195+
* @param {String} tokenV3 v3 auth token.
196+
* @return {Action}
197+
*/
198+
async function getStatsDistributionDone(handle, track, subTrack, uuid, tokenV3) {
199+
const data = await getService(tokenV3).getStatsDistribution(handle, track, subTrack);
200+
return { data, handle, uuid };
201+
}
202+
107203
export default createActions({
108204
MEMBERS: {
109205
DROP: drop,
@@ -114,5 +210,11 @@ export default createActions({
114210
GET_FINANCES_DONE: getFinancesDone,
115211
GET_STATS_INIT: getStatsInit,
116212
GET_STATS_DONE: getStatsDone,
213+
GET_STATS_HISTORY_INIT: getStatsHistoryInit,
214+
GET_STATS_HISTORY_DONE: getStatsHistoryDone,
215+
GET_STATS_DISTRIBUTION_INIT: getStatsDistributionInit,
216+
GET_STATS_DISTRIBUTION_DONE: getStatsDistributionDone,
217+
GET_ACTIVE_CHALLENGES_INIT: getActiveChallengesInit,
218+
GET_ACTIVE_CHALLENGES_DONE: getActiveChallengesDone,
117219
},
118220
});

src/reducers/members.js

+126
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,126 @@ function onGetStatsDone(state, { error, payload }) {
162162
};
163163
}
164164

165+
/**
166+
* Inits the loading of member stats history.
167+
* @param {Object} state
168+
* @param {Object} action
169+
* @return {Object} New state.
170+
*/
171+
function onGetStatsHistoryInit(state, action) {
172+
const { handle, uuid } = action.payload;
173+
let res = state[handle];
174+
res = res ? _.clone(res) : {};
175+
res.statsHistory = { loadingUuid: uuid };
176+
return {
177+
...state,
178+
[handle]: res,
179+
};
180+
}
181+
182+
/**
183+
* Finalizes the loading of member stats history.
184+
* @param {Object} state
185+
* @param {Object} action
186+
* @return {Object} New state.
187+
*/
188+
function onGetStatsHistoryDone(state, { error, payload }) {
189+
if (error) {
190+
logger.error('Failed to get member statsHistory', payload);
191+
fireErrorMessage('Failed to get member statsHistory', '');
192+
return state;
193+
}
194+
195+
const { data, handle, uuid } = payload;
196+
if (uuid !== _.get(state[handle], 'statsHistory.loadingUuid')) return state;
197+
return {
198+
...state,
199+
[handle]: {
200+
...state[handle],
201+
statsHistory: { data, timestamp: Date.now() },
202+
},
203+
};
204+
}
205+
206+
/**
207+
* Inits the loading of member stats distribution.
208+
* @param {Object} state
209+
* @param {Object} action
210+
* @return {Object} New state.
211+
*/
212+
function onGetStatsDistributionInit(state, action) {
213+
const { handle, uuid } = action.payload;
214+
let res = state[handle];
215+
res = res ? _.clone(res) : {};
216+
res.statsDistribution = { loadingUuid: uuid };
217+
return {
218+
...state,
219+
[handle]: res,
220+
};
221+
}
222+
223+
/**
224+
* Finalizes the loading of member stats distribution.
225+
* @param {Object} state
226+
* @param {Object} action
227+
* @return {Object} New state.
228+
*/
229+
function onGetStatsDistributionDone(state, { error, payload }) {
230+
if (error) {
231+
logger.error('Failed to get member statsDistribution', payload);
232+
fireErrorMessage('Failed to get member statsDistribution', '');
233+
return state;
234+
}
235+
236+
const { data, handle, uuid } = payload;
237+
if (uuid !== _.get(state[handle], 'statsDistribution.loadingUuid')) return state;
238+
return {
239+
...state,
240+
[handle]: {
241+
...state[handle],
242+
statsDistribution: { data, timestamp: Date.now() },
243+
},
244+
};
245+
}
246+
247+
/**
248+
* Inits the loading of member active challenges.
249+
* @param {Object} state
250+
* @param {Object} action
251+
* @return {Object} New state.
252+
*/
253+
function onGetActiveChallengesInit(state, action) {
254+
const { handle } = action.payload;
255+
return {
256+
...state,
257+
[handle]: { ...state[handle], activeChallengesCount: null },
258+
};
259+
}
260+
261+
/**
262+
* Finalizes the loading of member active challenges.
263+
* @param {Object} state
264+
* @param {Object} action
265+
* @return {Object} New state.
266+
*/
267+
function onGetActiveChallengesDone(state, { error, payload }) {
268+
if (error) {
269+
logger.error('Failed to get member active challenges', payload);
270+
fireErrorMessage('Failed to get member active challenges', '');
271+
return state;
272+
}
273+
274+
const { handle, challenges } = payload;
275+
276+
return {
277+
...state,
278+
[handle]: {
279+
...state[handle],
280+
activeChallengesCount: challenges.length,
281+
},
282+
};
283+
}
284+
165285
/**
166286
* Creates a new Members reducer with the specified initial state.
167287
* @param {Object} initialState Optional. Initial state.
@@ -178,6 +298,12 @@ function create(initialState = {}) {
178298
[a.getFinancesDone]: onGetFinancesDone,
179299
[a.getStatsInit]: onGetStatsInit,
180300
[a.getStatsDone]: onGetStatsDone,
301+
[a.getStatsHistoryInit]: onGetStatsHistoryInit,
302+
[a.getStatsHistoryDone]: onGetStatsHistoryDone,
303+
[a.getStatsDistributionInit]: onGetStatsDistributionInit,
304+
[a.getStatsDistributionDone]: onGetStatsDistributionDone,
305+
[a.getActiveChallengesInit]: onGetActiveChallengesInit,
306+
[a.getActiveChallengesDone]: onGetActiveChallengesDone,
181307
}, initialState);
182308
}
183309

src/services/members.js

+26
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* members via API V3.
55
*/
66

7+
import qs from 'qs';
78
import { getApiResponsePayloadV3 } from '../utils/tc';
89
import { getApiV3 } from './api';
910

@@ -84,6 +85,31 @@ class MembersService {
8485
return getApiResponsePayloadV3(res);
8586
}
8687

88+
/**
89+
* Gets member statistics history
90+
* @param {String} handle
91+
* @return {Promise} Resolves to the stats object.
92+
*/
93+
async getStatsHistory(handle) {
94+
const res = await this.private.api.get(`/members/${handle}/stats/history`);
95+
return getApiResponsePayloadV3(res);
96+
}
97+
98+
/**
99+
* Gets member statistics distribution
100+
* @param {String} handle
101+
* @param {String} track
102+
* @param {String} subTrack
103+
* @return {Promise} Resolves to the stats object.
104+
*/
105+
async getStatsDistribution(handle, track, subTrack) {
106+
const res = await this.private.api.get(`/members/stats/distribution?filter=${encodeURIComponent(qs.stringify({
107+
track,
108+
subTrack,
109+
}))}`);
110+
return getApiResponsePayloadV3(res);
111+
}
112+
87113
/**
88114
* Gets a list of suggested member names for the supplied partial.
89115
*

0 commit comments

Comments
 (0)