Как я могу бесконечно показывать модал через определенный промежуток времени?

#angular #rxjs

Вопрос:

Я пытаюсь запустить модал через 5 секунд, если пользователь остается неактивным, и написал для этого сервис, содержащий этот код:

 public initScreenListen(): void {
    this.mergedObservable$ = merge(
      fromEvent(document, BanKeyboardEvent.KEY_DOWN),
      fromEvent(document, BanMouseEvent.MOUSE_DOWN),
      fromEvent(document, BanMouseEvent.MOUSE_MOVE),
      fromEvent(document, BanMouseEvent.WHEEL),
      fromEvent(document, BanMouseEvent.CLIK),
      fromEvent(document, BanMouseEvent.MOUSE_MOVE),
      fromEvent(window, BanMouseEvent.MOUSE_MOVE)
    );
    this.startTimer();
  }
 public startTimer(): void {
    this.createObserable();
    console.log('subscription started');
  }

public createObserable(): void {
    this.ngZone.runOutsideAngular(() => {
      this.observeable$ = this.mergedObservable$.pipe(
        switchMap((ev) => interval(1000).pipe(take(this.inactivityTime))),
        tap((value) => this.isItTimeToShowPopUp(value)),
        skipWhile((x) => {
          this.timeLapsedSinceInactivity = x;
          return x !== this.inactivityTime - 1;
        })
      );
    });
  }

public isItTimeToShowPopUp(val: number): void {
    this.timeLeftForInactive = this.inactivityTime - val;
    if (this.timeLeftForInactive <= 13) {
      this.timeLapsedSinceInactivity = this.timeLeftForInactive;
      this.ref.tick();
      console.log(this.timeLeftForInactive);
    }
  }
 

и я написал это на самом компоненте:

 public ngOnInit(): void {
    this.inactivityListenService.initScreenListen();
    const subcription = this.inactivityListenService.observeable$.subscribe(
      () => {
        subcription.unsubscribe();
        console.log('here show modal.....');
        this.inactivityListenService.initScreenListen();
      }
    );
  }
 

и это работает только в первый раз, после этого он начинает вести себя беспорядочно.

Не могли бы вы помочь мне, пожалуйста?

Спасибо.

Комментарии:

1. Вы пробовали это npmjs.com/package/@ng-idle/core модуль ?

Ответ №1:

Я думаю, что у вас есть две проблемы в вашей реализации:

  • Вы не unsubscribe должны из основного observable , чтобы он работал все время, тогда нет необходимости снова вызывать this.inactivityListenService.initScreenListen() функцию после отображения модального.
  • Вы должны использовать filter вместо skipWhile внутри createObserable функции, потому skipWhile что разрешите каждое излучаемое значение после того, как оно впервые станет ложным, но filter всегда будете фильтровать каждое излучаемое значение и разрешать только те, которые соответствуют заданному условию.

Пропусти пока:

Возвращает наблюдаемое, которое пропускает все элементы, испускаемые наблюдаемым источником, до тех пор, пока указанное условие выполняется, но выдает все последующие элементы источника, как только условие становится ложным.

Фильтр:

Фильтруйте элементы, испускаемые наблюдаемым источником, испуская только те, которые удовлетворяют указанному предикату.

Вот части вашего кода, которые следует изменить:

 ngOnInit(): void {
  this.inactivityListenService.initScreenListen();
  this.inactivityListenService.observeable$.subscribe(() => {
    // >>>>> You shouldn't unsubscribe here.
    console.log('here show modal.....');
    // >>>>> There is no need to call initScreenListen again here.
  });
}
 
 public createObserable(): void {
  this.ngZone.runOutsideAngular(() => {
    this.observeable$ = this.mergedObservable$.pipe(
      switchMap(ev => interval(1000).pipe(take(this.inactivityTime))),
      tap(value => {
        this.isItTimeToShowPopUp(value);
      }),
      // >>>> use `filter` instead of `skipWhile`
      filter(x => {
        this.timeLapsedSinceInactivity = x;
        // >>> reflect the condition after using `filter`
        return x === this.inactivityTime - 1;
      })
    );
  });
}
 

А вот и рабочий стекблитц

Ответ №2:

Простой способ сделать это-создать наблюдаемое, которое излучает всякий раз, когда после 5000 мс не было никаких «действий». debounceTime Оператор отлично подойдет для этого:

 export class InactivityListenService {

  private activityTriggers$ = merge(
    fromEvent(document, 'mousemove'),
    fromEvent(document, 'keypress' ),
    fromEvent(window,   'mousemove'),
  );

  public idle$: Observable<void> = this.activityTriggers$.pipe(
    startWith(undefined),
    debounceTime(IDLE_THRESHOLD),
    mapTo(undefined)
  );

}
 

Здесь мы объединяем все activityTriggers$ использование merge , как вы уже делали.

Затем мы строим idle$ наблюдаемое на основе activityTriggers$ этого использования debounceTime для предотвращения выбросов до тех пор, пока не пройдет указанный интервал времени с момента получения выброса «активности».

Здесь я использовал mapTo(undefined) , так как излучение исходного события не кажется уместным (конечно, в этом нет необходимости).

Мы используем startWith , чтобы обеспечить начальную эмиссию для «запуска таймера» в случае, если пользователь вообще ничего не делает.

Теперь вы можете просто подписаться idle$ и выполнять свою работу (режим отображения).

Вот рабочая демонстрация StackBlitz.