Skip to content

Commit

Permalink
add catch to leaderboard (#2228)
Browse files Browse the repository at this point in the history
* add catch to leaderboard

* add error messages, and async/await to leaderboard related endpoint calls
  • Loading branch information
CollinBeczak authored Jan 9, 2024
1 parent 0d3702e commit c2b9bb5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 36 deletions.
22 changes: 17 additions & 5 deletions src/components/HOCs/WithLeaderboard/WithLeaderboard.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React, { Component } from 'react'
import { connect } from "react-redux"
import { bindActionCreators } from 'redux'
import _omit from 'lodash/omit'
import _isArray from 'lodash/isArray'
import _isBoolean from 'lodash/isBoolean'
import _map from 'lodash/map'
Expand All @@ -18,8 +21,8 @@ import { fetchLeaderboard, fetchLeaderboardForUser, fetchReviewerLeaderboard,
*
* @author [Neil Rotstan](https://github.com/nrotstan)
*/
const WithLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialOptions={}) {
return class extends Component {
const WithCurrentLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialOptions={}) {
class WithCurrentLeaderboardClass extends Component {
state = {
leaderboard: null,
leaderboardLoading: false,
Expand Down Expand Up @@ -89,7 +92,7 @@ const WithLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialO
this.setState({leaderboardLoading: true, showingCount, fetchId: currentFetch})

const fetch = userType === USER_TYPE_REVIEWER ?
fetchReviewerLeaderboard : fetchLeaderboard
this.props.fetchReviewerLeaderboard : this.props.fetchLeaderboard

fetch(...this.leaderboardParams(numberMonths, countryCode),
showingCount, startDate, endDate).then(leaderboard => {
Expand All @@ -98,7 +101,7 @@ const WithLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialO

const userId = _get(this.props, 'user.id')
if (userId && !options.ignoreUser && userType !== USER_TYPE_REVIEWER) {
fetchLeaderboardForUser(userId, 1,
this.props.fetchLeaderboardForUser(userId, 1,
...this.leaderboardParams(numberMonths, countryCode),
startDate, endDate).then(userLeaderboard => {
this.mergeInUserLeaderboard(userLeaderboard)
Expand Down Expand Up @@ -207,7 +210,8 @@ const WithLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialO
render() {
const moreResults = this.state.leaderboard ? this.state.showingCount <= this.state.leaderboard.length : true

return <WrappedComponent leaderboard={this.state.leaderboard}
return <WrappedComponent {..._omit(this.props, ['fetchLeaderboard', 'fetchLeaderboardForUser', 'fetchReviewerLeaderboard'])}
leaderboard={this.state.leaderboard}
leaderboardLoading={this.state.leaderboardLoading}
monthsPast={this.monthsPast()}
startDate={this.startDate()}
Expand All @@ -222,6 +226,14 @@ const WithLeaderboard = function(WrappedComponent, initialMonthsPast=1, initialO
{...this.props} />
}
}
return WithCurrentLeaderboardClass;
}

const mapDispatchToProps = (dispatch) => bindActionCreators({ fetchLeaderboard, fetchLeaderboardForUser, fetchReviewerLeaderboard }, dispatch)


const WithLeaderboard = (WrappedComponent) =>
connect(null, mapDispatchToProps)(WithCurrentLeaderboard(WrappedComponent))

export default WithLeaderboard

8 changes: 4 additions & 4 deletions src/components/HOCs/WithUserMetrics/WithUserMetrics.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import format from 'date-fns/format'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import _omit from 'lodash/omit'
import _get from 'lodash/get'
import { fetchLeaderboardForUser } from '../../../services/Leaderboard/Leaderboard'
Expand Down Expand Up @@ -31,7 +32,7 @@ export const WithUserMetrics = function(WrappedComponent, userProp) {
if ( this.props[userProp] &&
(!_get(this.props[userProp], 'settings.leaderboardOptOut') ||
_get(this.props[userProp], 'id') === _get(this.props.currentUser, 'userId'))) {
fetchLeaderboardForUser(this.props[userProp].id, 0, -1).then(userLeaderboard => {
this.props.fetchLeaderboardForUser(this.props[userProp].id, 0, -1).then(userLeaderboard => {
this.setState({loading: false, leaderboardMetrics: userLeaderboard[0]})
})

Expand Down Expand Up @@ -182,15 +183,14 @@ export const WithUserMetrics = function(WrappedComponent, userProp) {
setTasksReviewerMonthsPast={this.setTasksReviewerMonthsPast}
setTasksReviewerDateRange={this.setTasksReviewerDateRange}
loading={this.state.loading}
{..._omit(this.props, ['updateLeaderboardMetrics'])} />)
{..._omit(this.props, ['updateLeaderboardMetrics', 'fetchLeaderboardForUser'])} />)
}
}
}

const mapStateToProps = () => ({})

const mapDispatchToProps = () => ({
})
const mapDispatchToProps = (dispatch) => bindActionCreators({ fetchLeaderboardForUser }, dispatch)

export default (WrappedComponent, userProp="targetUser") =>
connect(mapStateToProps, mapDispatchToProps)(WithUserMetrics(WrappedComponent, userProp))
2 changes: 2 additions & 0 deletions src/services/Error/AppErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default {

leaderboard: {
fetchFailure: messages.leaderboardFetchFailure,
reviewerLeaderboard: messages.reviewerLeaderboard,
userFetchFailure: messages.userFetchFailure,
},

task: {
Expand Down
10 changes: 10 additions & 0 deletions src/services/Error/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ export default defineMessages({
defaultMessage: "Unable to fetch leaderboard.",
},

reviewerLeaderboard: {
id: "Errors.leaderboard.reviewerLeaderboard",
defaultMessage: "Unable to retrieve reviewer leaderboard data.",
},

userFetchFailure: {
id: "Errors.leaderboard.userFetchFailure",
defaultMessage: "Unable to retrieve leaderboard data for user.",
},

taskNone: {
id: "Errors.task.none",
defaultMessage: "No tasks remain in this challenge.",
Expand Down
83 changes: 56 additions & 27 deletions src/services/Leaderboard/Leaderboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import startOfMonth from 'date-fns/start_of_month'
import endOfDay from 'date-fns/end_of_day'
import { CHALLENGE_INCLUDE_LOCAL } from '../Challenge/Challenge'
import { setupCustomCache } from '../../utils/setupCustomCache'
import { addError } from '../Error/Error'
import AppErrors from '../Error/AppErrors'

// Default leaderboard count
export const DEFAULT_LEADERBOARD_COUNT = 10
Expand Down Expand Up @@ -33,39 +35,47 @@ const leaderboardCache = setupCustomCache(CACHE_TIME);
* filters, returning a Promise that resolves to the leaderboard data. Note
* that leaderboard data is *not* stored in the redux store.
*/
export const fetchLeaderboard = async (numberMonths=null, onlyEnabled=true,
forProjects=null, forChallenges=null,
forUsers=null, forCountries=null,
limit=10, startDate=null, endDate=null) => {
export const fetchLeaderboard = (numberMonths=null, onlyEnabled=true,
forProjects=null, forChallenges=null,
forUsers=null, forCountries=null,
limit=10, startDate=null, endDate=null) => {
const params = {
limit,
onlyEnabled
}

initializeLeaderboardParams(params, numberMonths, forProjects, forChallenges,
forUsers, forCountries, startDate, endDate)
return async function (dispatch) {
initializeLeaderboardParams(params, numberMonths, forProjects, forChallenges,
forUsers, forCountries, startDate, endDate)

const cachedLeaderboard = leaderboardCache.get({}, params, GLOBAL_LEADERBOARD_CACHE);
const cachedLeaderboard = leaderboardCache.get({}, params, GLOBAL_LEADERBOARD_CACHE);

if (cachedLeaderboard) {
return cachedLeaderboard;
}
if (cachedLeaderboard) {
return cachedLeaderboard;
}

const results = await new Endpoint(api.users.leaderboard, {params}).execute()
try {
const results = await new Endpoint(api.users.leaderboard, { params }).execute()

if (results) {
leaderboardCache.set({}, params, results, GLOBAL_LEADERBOARD_CACHE)
}
if (results) {
leaderboardCache.set({}, params, results, GLOBAL_LEADERBOARD_CACHE)
}

return results
return results
} catch (error) {
console.error('Error fetching leaderboard:', error)
dispatch(addError(AppErrors.leaderboard.fetchFailure))
return []
}
}
}

/**
* Retrieve leaderboard data for a user from the server for the given date range and
* filters, returning a Promise that resolves to the leaderboard data. Note
* that leaderboard data is *not* stored in the redux store.
*/
export const fetchLeaderboardForUser = async (userId, bracket=0, numberMonths=1,
export const fetchLeaderboardForUser = (userId, bracket=0, numberMonths=1,
onlyEnabled=true, forProjects=null, forChallenges=null,
forUsers, forCountries=null, startDate=null,
endDate=null) => {
Expand All @@ -74,6 +84,8 @@ export const fetchLeaderboardForUser = async (userId, bracket=0, numberMonths=1,
onlyEnabled
}

return async function (dispatch) {

const variables = {
id: userId
}
Expand All @@ -87,33 +99,50 @@ export const fetchLeaderboardForUser = async (userId, bracket=0, numberMonths=1,
return cachedLeaderboard;
}

const results = await new Endpoint(api.users.userLeaderboard, {variables, params}).execute()
try {
const results = await new Endpoint(api.users.userLeaderboard, {variables, params}).execute()

if (results) {
leaderboardCache.set(variables, params, results, USER_LEADERBOARD_CACHE)
}
if (results) {
leaderboardCache.set(variables, params, results, USER_LEADERBOARD_CACHE)
}

return results;
return results;
} catch (error) {
console.error('Error fetching leaderboard:', error)
dispatch(addError(AppErrors.leaderboard.userFetchFailure))
return null
}
}
}

/**
* Retrieve reviewer leaderboard data from the server for the given date range and
* filters, returning a Promise that resolves to the leaderboard data. Note
* that leaderboard data is *not* stored in the redux store.
*/
export const fetchReviewerLeaderboard = function(numberMonths=null, onlyEnabled=true,
export const fetchReviewerLeaderboard = (numberMonths=null, onlyEnabled=true,
forProjects=null, forChallenges=null,
forUsers=null, forCountries=null,
limit=10, startDate=null, endDate=null) {
limit=10, startDate=null, endDate=null) => {

const params = {
limit,
onlyEnabled
}

initializeLeaderboardParams(params, numberMonths, forProjects, forChallenges,
forUsers, forCountries, startDate, endDate)

return new Endpoint(api.users.reviewerLeaderboard, {params}).execute()
return async function (dispatch) {
try {

initializeLeaderboardParams(params, numberMonths, forProjects, forChallenges,
forUsers, forCountries, startDate, endDate)
const result = await new Endpoint(api.users.reviewerLeaderboard, {params}).execute()
return result
} catch (error) {
console.error("Error in fetchReviewerLeaderboard:", error)
dispatch(addError(AppErrors.leaderboard.reviewerLeaderboard))
return []
}
}
}


Expand Down

0 comments on commit c2b9bb5

Please sign in to comment.