Skip to content

Commit 640c60d

Browse files
authored
Merge pull request #223 from nrotstan/renew-virtual-challenge-when-activity
Renew virtual challenge when new activity.
2 parents 6fdc49b + 7fe7a5e commit 640c60d

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ REACT_APP_NEARBY_LATITUDE_LENGTH=0.75
3535
# are browsing map-bounded tasks.
3636
REACT_APP_BOUNDED_TASKS_MAX_DIMENSION=2.5
3737

38+
# Default time, in hours, until a newly-created virtual challenge expires.
39+
REACT_APP_VIRTUAL_CHALLENGE_DURATION=36
40+
3841
# iD editor base URL
3942
REACT_APP_ID_EDITOR_SERVER_URL='https://www.openstreetmap.org/edit'
4043

src/components/HOCs/WithCurrentTask/WithCurrentTask.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { TaskLoadMethod }
1717
from '../../../services/Task/TaskLoadMethod/TaskLoadMethod'
1818
import { contactOSMUserURL } from '../../../services/OSMUser/OSMUser'
1919
import { fetchChallengeActions } from '../../../services/Challenge/Challenge'
20+
import { renewVirtualChallenge }
21+
from '../../../services/VirtualChallenge/VirtualChallenge'
2022

2123
const FRESHNESS_THRESHOLD = 5000 // 5 seconds
2224

@@ -107,6 +109,12 @@ export const mapDispatchToProps = (dispatch, ownProps) => {
107109
dispatch(addTaskComment(taskId, comment, taskStatus))
108110
}
109111
dispatch(fetchChallengeActions(challengeId))
112+
113+
// If working on a virtual challenge, renew it (extend its expiration)
114+
// since we've seen some activity.
115+
if (_isFinite(ownProps.virtualChallengeId)) {
116+
dispatch(renewVirtualChallenge(ownProps.virtualChallengeId))
117+
}
110118
})
111119

112120
// Load the next task from the challenge.

src/services/Server/APIRoutes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const apiRoutes = factory => {
5555
'virtualChallenge': {
5656
'single': factory.get('/virtualchallenge/:id'),
5757
'create': factory.post('/virtualchallenge'),
58+
'edit': factory.put('/virtualchallenge/:id'),
5859
'randomTask': factory.get('/virtualchallenge/:id/task'),
5960
'clusteredTasks': factory.get('/virtualchallenge/clustered/:id'),
6061
},

src/services/VirtualChallenge/VirtualChallenge.js

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export const virtualChallengeSchema = function() {
1515
return new schema.Entity('virtualChallenges')
1616
}
1717

18+
/*
19+
* Time, in hours, until new virtual challenges expire. Defaults to 36 hours if
20+
* nothing is specified in the .env file.
21+
*/
22+
export const DEFAULT_EXPIRATION_DURATION=
23+
parseInt(_get(process.env, 'REACT_APP_VIRTUAL_CHALLENGE_DURATION', 36), 10)
24+
1825
// redux actions
1926
const RECEIVE_VIRTUAL_CHALLENGES = 'RECEIVE_VIRTUAL_CHALLENGES'
2027

@@ -59,39 +66,78 @@ export const fetchVirtualChallenge = function(virtualChallengeId) {
5966
}
6067

6168
/**
62-
* Creates a new virtual challenge with the given name and tasks.
69+
* Creates a new virtual challenge with the given name and tasks. If an
70+
* explicit expiration timestamp is given, it'll be used; otherwise the virtual
71+
* challenge will be set to expire after the default configured duration.
6372
*/
6473
export const createVirtualChallenge = function(name, taskIds, expiration) {
6574
return function(dispatch) {
6675
const challengeData = {
6776
name,
6877
taskIdList: taskIds,
69-
expiry: expiration ? expiration : addHours(new Date(), 36).getTime()
78+
expiry: expiration ? expiration :
79+
addHours(new Date(), DEFAULT_EXPIRATION_DURATION).getTime()
7080
}
7181

72-
return new Endpoint(api.virtualChallenge.create, {
73-
schema: virtualChallengeSchema(),
74-
json: challengeData,
75-
}).execute().then(normalizedResults => {
76-
dispatch(receiveVirtualChallenges(normalizedResults.entities))
77-
return _get(normalizedResults,
78-
`entities.virtualChallenges.${normalizedResults.result}`)
79-
}).catch((serverError) => {
80-
if (serverError.response && serverError.response.status === 401) {
81-
// If we get an unauthorized, we assume the user is not logged
82-
// in (or no longer logged in with the server).
83-
dispatch(logoutUser())
84-
dispatch(addError(AppErrors.user.unauthorized))
85-
}
86-
else {
87-
console.log(serverError.response || serverError)
88-
dispatch(addServerError(AppErrors.virtualChallenge.createFailure,
89-
serverError))
90-
}
91-
})
82+
return saveVirtualChallenge(
83+
dispatch,
84+
new Endpoint(api.virtualChallenge.create, {
85+
schema: virtualChallengeSchema(),
86+
json: challengeData,
87+
})
88+
)
89+
}
90+
}
91+
92+
/**
93+
* Renews the expiration time of the virtual challenge, either setting it to
94+
* the explicit expiration timestamp given or resetting it to the default
95+
* configured duration if no expiration is specified.
96+
*/
97+
export const renewVirtualChallenge = function(virtualChallengeId, expiration) {
98+
return function(dispatch) {
99+
const challengeData = {
100+
expiry: expiration ? expiration :
101+
addHours(new Date(), DEFAULT_EXPIRATION_DURATION).getTime()
102+
}
103+
104+
return saveVirtualChallenge(
105+
dispatch,
106+
new Endpoint(api.virtualChallenge.edit, {
107+
variables: {id: virtualChallengeId},
108+
schema: virtualChallengeSchema(),
109+
json: challengeData,
110+
})
111+
)
92112
}
93113
}
94114

115+
/**
116+
* Executes the given endpoint, saving the virtual challenge, and
117+
* processes the response.
118+
*
119+
* @private
120+
*/
121+
export const saveVirtualChallenge = function(dispatch, endpoint) {
122+
return endpoint.execute().then(normalizedResults => {
123+
dispatch(receiveVirtualChallenges(normalizedResults.entities))
124+
return _get(normalizedResults,
125+
`entities.virtualChallenges.${normalizedResults.result}`)
126+
}).catch((serverError) => {
127+
if (serverError.response && serverError.response.status === 401) {
128+
// If we get an unauthorized, we assume the user is not logged
129+
// in (or no longer logged in with the server).
130+
dispatch(logoutUser())
131+
dispatch(addError(AppErrors.user.unauthorized))
132+
}
133+
else {
134+
console.log(serverError.response || serverError)
135+
dispatch(addServerError(AppErrors.virtualChallenge.createFailure,
136+
serverError))
137+
}
138+
})
139+
}
140+
95141
// redux reducers
96142
//
97143
/**

0 commit comments

Comments
 (0)