Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Use visibility API instead of beforeunload #158

Merged
merged 9 commits into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The `fusion-plugin-universal-events` is commonly required by other Fusion.js plu

It's useful for when you want to collect data about user actions or other metrics, and send them in bulk to the server to minimize the number of HTTP requests.

For convenience, this plugin automatically flushes its queue on page unload.
For convenience, this plugin automatically flushes its queue before page unload on `document.visibilityState === 'hidden'`.

If you need to use the universal event emitter from React, use [`fusion-plugin-universal-events-react`](https://github.com/fusionjs/fusion-plugin-universal-events-react)

Expand Down
11 changes: 8 additions & 3 deletions src/__tests__/test.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @flow
*/

/* eslint-env browser */
import test from 'tape-cup';

import App from 'fusion-core';
Expand All @@ -20,6 +21,10 @@ import {
inMemoryBatchStorage as store,
} from '../storage/index.js';

// Set document.visibilityState to test flushBeforeTerminated
Object.defineProperty(document, 'visibilityState', {value: 'hidden'});
const visibilitychangeEvent = new Event('visibilitychange');

/* Test helpers */
function getApp(fetch: Fetch) {
const app = new App('el', el => el);
Expand Down Expand Up @@ -82,7 +87,7 @@ test('Browser EventEmitter', async t => {
emitted = true;
});
emitter.emit('a', {x: 1});
emitter.flush();
window.dispatchEvent(visibilitychangeEvent);
emitter.teardown();
return next();
};
Expand All @@ -107,7 +112,7 @@ test('Browser EventEmitter adds events back to queue if they fail to send', asyn
const emitter = events.from(ctx);
t.equal(emitter, events);
emitter.emit('a', {x: 1});
emitter.flush();
window.dispatchEvent(visibilitychangeEvent);
emitter.teardown();
return next();
};
Expand All @@ -130,7 +135,7 @@ test('Browser EventEmitter adds events back to queue if they fail to send 2', as
const emitter = events.from(ctx);
t.equal(emitter, events);
emitter.emit('a', {x: 1});
emitter.flush();
window.dispatchEvent(visibilitychangeEvent);
emitter.teardown();
return next();
};
Expand Down
8 changes: 5 additions & 3 deletions src/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class UniversalEmitter extends Emitter {
this.flush = this.flushInternal.bind(this);
this.fetch = fetch;
this.setFrequency(5000);
window.addEventListener('beforeunload', this.flush);
window.addEventListener('visibilitychange', this.flushBeforeTerminated);
}
setFrequency(frequency: number): void {
window.clearInterval(this.interval);
this.interval = setInterval(this.flush, frequency);
this.interval = setInterval(this.flushInternal, frequency);
}
emit(type: mixed, payload: mixed): void {
payload = super.mapEvent(type, payload);
Expand All @@ -50,6 +50,8 @@ class UniversalEmitter extends Emitter {
from(): UniversalEmitter {
return this;
}
flushBeforeTerminated = () =>
document.visibilityState === 'hidden' && this.flushInternal();
async flushInternal(): Promise<void> {
const items = this.storage.getAndClear();
if (items.length === 0) return;
Expand All @@ -73,7 +75,7 @@ class UniversalEmitter extends Emitter {
}
}
teardown(): void {
window.removeEventListener('beforeunload', this.flush);
window.removeEventListener('visibilitychange', this.flushBeforeTerminated);
clearInterval(this.interval);
this.interval = null;
}
Expand Down