Skip to content

Commit

Permalink
Pause refresh when page is hidden (#177693)
Browse files Browse the repository at this point in the history
## Summary

close #1878

Pauses auto-refresh when a page is not visible. 
I tested and didn't notice any issues, but looking for more testing help


## Release Notes

Auto-refresh pauses when the page is not visible.
  • Loading branch information
Dosant committed Mar 4, 2024
1 parent c970b39 commit b635674
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,39 @@ test('calling older done() is ignored', () => {
jest.advanceTimersByTime(501);
expect(fn1).toHaveBeenCalledTimes(2);
});

test('pauses if page is not visible', () => {
let mockPageVisibility: DocumentVisibilityState = 'visible';
jest.spyOn(document, 'visibilityState', 'get').mockImplementation(() => mockPageVisibility);

const { loop$, start, stop } = createAutoRefreshLoop();

const fn = jest.fn((done) => done());
loop$.subscribe(fn);

jest.advanceTimersByTime(5000);
expect(fn).not.toBeCalled();

start(1000);

jest.advanceTimersByTime(1001);
expect(fn).toHaveBeenCalledTimes(1);

mockPageVisibility = 'hidden';
document.dispatchEvent(new Event('visibilitychange'));

jest.advanceTimersByTime(1001);
expect(fn).toHaveBeenCalledTimes(1);

mockPageVisibility = 'visible';
document.dispatchEvent(new Event('visibilitychange'));
expect(fn).toHaveBeenCalledTimes(2);

jest.advanceTimersByTime(1001);
expect(fn).toHaveBeenCalledTimes(3);

stop();

jest.advanceTimersByTime(5000);
expect(fn).toHaveBeenCalledTimes(3);
});
13 changes: 11 additions & 2 deletions src/plugins/data/public/query/timefilter/lib/auto_refresh_loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
*/

import { defer, Subject } from 'rxjs';
import { finalize, map } from 'rxjs/operators';
import { finalize, map, delayWhen, filter } from 'rxjs/operators';
import { once } from 'lodash';
import { createPageVisibility$ } from './page_visibility';

export type AutoRefreshDoneFn = () => void;

Expand All @@ -18,6 +19,8 @@ export type AutoRefreshDoneFn = () => void;
* When auto refresh loop emits, it won't continue automatically,
* until each subscriber calls received `done` function.
*
* Also, it will pause when the page is not visible.
*
* @internal
*/
export const createAutoRefreshLoop = () => {
Expand Down Expand Up @@ -51,6 +54,12 @@ export const createAutoRefreshLoop = () => {
_timeoutHandle = -1;
}

const pageVisible$ = createPageVisibility$().pipe(
filter((visibility) => visibility === 'visible')
);

const tickWhenVisible$ = tick.pipe(delayWhen(() => pageVisible$));

return {
stop: () => {
_timeout = 0;
Expand All @@ -65,7 +74,7 @@ export const createAutoRefreshLoop = () => {
loop$: defer(() => {
subscribersCount++;
start(); // restart the loop on a new subscriber
return tick.pipe(map((doneCb) => once(doneCb))); // each subscriber allowed to call done only once
return tickWhenVisible$.pipe(map((doneCb) => once(doneCb))); // each subscriber allowed to call done only once
}).pipe(
finalize(() => {
subscribersCount--;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { createPageVisibility$ } from './page_visibility';

let mockPageVisibility: DocumentVisibilityState = 'visible';
jest.spyOn(document, 'visibilityState', 'get').mockImplementation(() => mockPageVisibility);

test('createPageVisibility$ returns an observable that emits visibility state', () => {
const pageVisibility$ = createPageVisibility$();

const fn = jest.fn();
pageVisibility$.subscribe(fn);

expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith('visible');

mockPageVisibility = 'hidden';
document.dispatchEvent(new Event('visibilitychange'));

expect(fn).toHaveBeenCalledTimes(2);
expect(fn).toHaveBeenLastCalledWith('hidden');
});
18 changes: 18 additions & 0 deletions src/plugins/data/public/query/timefilter/lib/page_visibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { fromEvent, Observable } from 'rxjs';
import { map, shareReplay, startWith } from 'rxjs/operators';

export function createPageVisibility$(): Observable<DocumentVisibilityState> {
return fromEvent(document, 'visibilitychange').pipe(
startWith(document.visibilityState),
map(() => document.visibilityState),
shareReplay({ refCount: true, bufferSize: 1 })
);
}

0 comments on commit b635674

Please sign in to comment.