问题
I'm trying to adapt some code I've created for a session timeout. Whenever a user interacts with the function, I want to reset the timer to start counting down again. I'm having difficulty working out how to inject similar code to this example
C# code - I'm trying to integrate:
var update = new Subject<bool>();
var res =
update
.Select(x => Observable.Interval(TimeSpan.FromSeconds(10.0)))
.Switch();
res
.Subscribe(_ => Console.WriteLine("Status sent."));
update.OnNext(true);
res
.Subscribe(_ =>
{
update.OnNext(true);
Console.WriteLine("Status sent.");
});
From what I can tell the Typescript RxJS equivalents of C# flavour of the RxJS library and my variable name mapping are:
C#/example Typescript/mine
update resetManager
.Select .map
(from here - (see tab title)).OnNext .next
.Interval .timer
I was thinking I could insert a do and chain the resetManager - but since one is a boolean and the other is AuthState I seem to be running into difficulties.
I'm wondering if a RxJS guru can advise. Cheers.
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
import 'rxjs/add/Observable/timer';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switch';
@Injectable()
export class AuthService {
private authState: AuthState;
private authManager: BehaviorSubject<AuthState>;
public authChange$: Observable<AuthState>;
private resetManager: Subject<boolean>;
constructor() {
this.authManager = new BehaviorSubject(AuthState.LOGGED_OUT);
this.authChange$ = this.authManager.asObservable();
this.authChange$
.filter((authState:AuthState) => authState === AuthState.LOGGED_IN)
.map( (authState:AuthState) => Observable.timer(SESSION_TIMEOUT))
.do( () =>
console.log('Logged In. Session Timout counting down from now'))
.switch()
.subscribe( () => {console.log('Timer ended: Logging out')
this.logout();
});
}
login() {
this.setAuthState(AuthState.LOGGED_IN);
}
logout() {
this.setAuthState(AuthState.LOGGED_OUT);
}
resetSessionTimer() {
this.resetManager.next(true);
}
emitAuthState():void {
this.authManager.next(this.authState);
}
private setAuthState(newAuthState:AuthState):void {
console.log('AuthService: setAuthState: ',
AuthState[newAuthState.toString()]);
if (newAuthState != this.authState) {
this.authState = newAuthState;
this.emitAuthState();
}
}
export enum AuthState {
LOGGED_IN,
LOGGED_OUT
}
const SESSION_TIMEOUT = 5000;
回答1:
I finally worked out how to do this...
import { Events } from 'ionic-angular';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/mapTo';
import 'rxjs/add/observable/defer';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/merge';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/switchMap';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export enum AuthState {
LOGGED_IN,
LOGGED_OUT
}
const SESSION_TIMEOUT = 10; // seconds
export class AuthService {
private timer: number;
private tick$: Observable<number>;
private countDown$: Observable<number>;
private subscriptions:Subscription[];
constructor(
private events:Events
) {
this.tick$ =Observable.interval(1000).mapTo(-1);
this.countDown$ = Observable
.defer(() => Observable.of(this.timer))
.merge(this.tick$)
.scan((acc,curr) => acc + curr)
this.events.subscribe(RESET_COUNTDOWN, (action:string) => {
this.resetSessionTimer(action);
});
}
beginCountDown() {
let sub:Subscription;
while (this.subscriptions.length > 0) {
sub = this.subscriptions.pop();
sub.unsubscribe();
};
this.timer = SESSION_TIMEOUT;
sub = this.countDown$
.filter(timer => timer >= 0)
.subscribe(timer =>
{this.timer = timer;
if (this.timer === 1) {
this.logout();
}
console.log(this.timer);
});
this.subscriptions.push(sub);
}
resetSessionTimer(action:string) {
console.log('AuthService: resetSessionTimer(): ' + action);
this.beginCountDown();
}
emitAuthState():void {
this.authManager.next(this.authState);
}
private setAuthState(newAuthState:AuthState):void {
if (newAuthState != this.authState) {
this.authState = newAuthState;
this.emitAuthState();
}
}
login(
clickStream$: BehaviorSubject<any>
) {
this.response$ = clickStream$.switchMap(click => {
return this.webServiceUtil.testLogin();
}
);
this.response$.subscribe((response:Response) => {
this.setAuthState(AuthState.LOGGED_IN);
},
(err:any) => {
this.setAuthState(AuthState.LOGGED_OUT);
})
}
logout() {
if (this.authState === AuthState.LOGGED_IN) {
this.setAuthState(AuthState.LOGGED_OUT);
}
}
}
A RESET_COUNTDOWN event can get fired on things like a change of page or when doing HTTP requests. That will force the countdown to begin again, whilst only ever having one subscription, hence the keeping track of an array of subscriptions.. That you immediately cancel on reset.
来源:https://stackoverflow.com/questions/45816050/implementing-session-timeout-service-for-ionic-angular-with-timer-reset-with-ea