1
1
import Service , { inject as service } from '@ember/service' ;
2
2
import { tracked } from '@glimmer/tracking' ;
3
3
4
- import { dropTask , race , rawTimeout , task , waitForEvent } from 'ember-concurrency' ;
4
+ import { dropTask , race , rawTimeout , restartableTask , task , waitForEvent } from 'ember-concurrency' ;
5
5
import window from 'ember-window-mock' ;
6
6
import { alias } from 'macro-decorators' ;
7
7
@@ -45,7 +45,7 @@ export default class SessionService extends Service {
45
45
}
46
46
47
47
get isSudoEnabled ( ) {
48
- return this . currentUser ?. is_admin === true && this . sudoEnabledUntil !== null && this . sudoEnabledUntil >= Date . now ( ) ;
48
+ return this . currentUser ?. is_admin === true && this . sudoTask . isRunning ;
49
49
}
50
50
51
51
/**
@@ -58,14 +58,12 @@ export default class SessionService extends Service {
58
58
* immediately.
59
59
*/
60
60
setSudo ( duration_ms ) {
61
- if ( this . currentUser ?. is_admin ) {
61
+ if ( this . isAdmin ) {
62
62
if ( duration_ms ) {
63
- const expiry = Date . now ( ) + duration_ms ;
64
- localStorage . setItem ( 'sudo' , expiry ) ;
65
- this . sudoEnabledUntil = expiry ;
63
+ // eslint-disable-next-line ember-concurrency/no-perform-without-catch
64
+ this . sudoTask . perform ( Date . now ( ) + duration_ms ) ;
66
65
} else {
67
- localStorage . removeItem ( 'sudo' ) ;
68
- this . sudoEnabledUntil = null ;
66
+ this . sudoTask . cancelAll ( ) ;
69
67
}
70
68
}
71
69
}
@@ -204,7 +202,10 @@ export default class SessionService extends Service {
204
202
const expiry = localStorage . getItem ( 'sudo' ) ;
205
203
if ( expiry !== null ) {
206
204
try {
207
- this . sudoEnabledUntil = + expiry ;
205
+ // Trigger sudoTask, but without waiting for it to complete.
206
+ //
207
+ // eslint-disable-next-line ember-concurrency/no-perform-without-catch
208
+ this . sudoTask . perform ( + expiry ) ;
208
209
} catch {
209
210
// It doesn't really matter if this fails; any invalid value will just
210
211
// be treated as the user not being in sudo mode.
@@ -214,4 +215,30 @@ export default class SessionService extends Service {
214
215
215
216
return { currentUser, ownedCrates } ;
216
217
} ) ;
218
+
219
+ sudoTask = restartableTask ( async until => {
220
+ try {
221
+ const now = Date . now ( ) ;
222
+
223
+ if ( until > now ) {
224
+ // Since this task will replace any running task, we should update local
225
+ // storage.
226
+ localStorage . setItem ( 'sudo' , until . toString ( ) ) ;
227
+
228
+ // We'll also surface the expiry as a property on the session service,
229
+ // since that can be tracked and updated by other components.
230
+ this . sudoEnabledUntil = until ;
231
+
232
+ // Now we sleep until sudo mode has expired.
233
+ await rawTimeout ( until - now ) ;
234
+ }
235
+ } finally {
236
+ // Clear the local storage, since we're no longer in sudo mode, regardless
237
+ // of whether the await finished or the task was cancelled.
238
+ localStorage . removeItem ( 'sudo' ) ;
239
+
240
+ // Again, update the session service property.
241
+ this . sudoEnabledUntil = null ;
242
+ }
243
+ } ) ;
217
244
}
0 commit comments