Support lazy rendering (#2109)

* feat: support lazy rendering

* fix: added new env var to onbuild args

* review: apply suggestions
This commit is contained in:
Kiwi
2018-12-06 21:24:31 +00:00
committed by Wyatt Johnson
parent 08d342ea2e
commit 362f29f77e
7 changed files with 95 additions and 15 deletions
+1
View File
@@ -11,6 +11,7 @@ ONBUILD ARG TALK_DEFAULT_LANG=en
ONBUILD ARG TALK_WHITELISTED_LANGUAGES
ONBUILD ARG TALK_PLUGINS_JSON
ONBUILD ARG TALK_WEBPACK_SOURCE_MAP
ONBUILD ARG TALK_DEFAULT_LAZY_RENDER
# Bundle app source
ONBUILD COPY . /usr/src/app
+64 -15
View File
@@ -3,6 +3,7 @@ import pym from 'pym.js';
import EventEmitter from 'eventemitter2';
import { buildUrl } from 'coral-framework/utils/url';
import Snackbar from './Snackbar';
import onIntersect from './onIntersect';
import {
createStorage,
connectStorageToPym,
@@ -50,15 +51,62 @@ export default class Stream {
...opts
} = config;
this.onAuthChanged = onAuthChanged;
this.el = el;
this.talkBaseUrl = talkBaseUrl;
this.opts = opts;
this.emitter = new EventEmitter({ wildcard: true });
this.pym = new pym.Parent(el.id, buildStreamIframeUrl(talkBaseUrl, query), {
title: opts.title,
id: `${el.id}_iframe`,
name: `${el.id}_iframe`,
});
this.snackBar = new Snackbar(snackBarStyles || {});
this.emitter = new EventEmitter({ wildcard: true });
// Attach to the events emitted by the pym parent.
if (events) {
events(this.emitter);
}
if (config.lazy || process.env.TALK_DEFAULT_LAZY_RENDER === 'TRUE') {
const renderOnIntersect = () => onIntersect(this.el, () => this.render());
if (!window.IntersectionObserver) {
// Include a polyfill for the intersection observer.
import('intersection-observer')
.then(() => {
// Polyfill applied.
renderOnIntersect();
})
.catch(e => {
console.error(e);
// Loading polyfill failed, just render it directly.
this.render();
});
} else {
// No need for polyfill.
renderOnIntersect();
}
} else {
this.render();
}
}
assertRendered() {
if (!this.pym) {
throw new Error('Stream Embed must be rendered first');
}
}
isRendered() {
return !!this.pym;
}
render() {
if (this.pym) {
throw new Error('Stream Embed already rendered');
}
this.pym = new pym.Parent(
this.el.id,
buildStreamIframeUrl(this.talkBaseUrl, this.query),
{
title: this.opts.title,
id: `${this.el.id}_iframe`,
name: `${this.el.id}_iframe`,
}
);
// Workaround: IOS Safari ignores `width` but respects `min-width` value.
this.pym.el.firstChild.style.width = '1px';
@@ -73,19 +121,14 @@ export default class Stream {
}
});
// Attach to the events emitted by the pym parent.
if (events) {
events(this.emitter);
}
this.pym.onMessage('getConfig', () => {
this.pym.sendMessage('config', JSON.stringify(opts));
this.pym.sendMessage('config', JSON.stringify(this.opts));
});
// If the auth changes, and someone is listening for it, then re-emit it.
if (onAuthChanged) {
if (this.onAuthChanged) {
this.pym.onMessage('coral-auth-changed', message => {
onAuthChanged(message ? JSON.parse(message) : null);
this.onAuthChanged(message ? JSON.parse(message) : null);
});
}
@@ -163,22 +206,27 @@ export default class Stream {
}
enablePluginsDebug() {
this.assertRendered();
this.pym.sendMessage('enablePluginsDebug');
}
disablePluginsDebug() {
this.assertRendered();
this.pym.sendMessage('disablePluginsDebug');
}
login(token) {
this.assertRendered();
this.pym.sendMessage('login', token);
}
logout() {
this.assertRendered();
this.pym.sendMessage('logout');
}
remove() {
this.assertRendered();
// Remove the event listeners.
document.removeEventListener('click', this.handleClick.bind(this));
this.emitter.removeAllListeners();
@@ -191,6 +239,7 @@ export default class Stream {
}
handleClick() {
this.assertRendered();
this.pym.sendMessage('click');
}
}
+1
View File
@@ -34,6 +34,7 @@ export class Talk {
* @param {String} [config.asset_url] - Asset URL
* @param {String} [config.asset_id] - Asset ID
* @param {String} [config.auth_token] - (optional) A jwt representing the session
* @param {String} [config.lazy] - (optional) If set the stream will only render lazily.
* @return {Object}
*
* Example:
+20
View File
@@ -0,0 +1,20 @@
export default function onIntersect(el, callback) {
if (!IntersectionObserver) {
// tslint:disable-next-line:no-console
console.warn('IntersectionObserver not available');
callback();
return;
}
const options = {
rootMargin: '100px',
threshold: 1.0,
};
const observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
observer.disconnect();
callback();
}
}, options);
observer.observe(el);
}
+1
View File
@@ -237,6 +237,7 @@
"eslint-plugin-mocha": "^4.11.0",
"husky": "^0.14.3",
"identity-obj-proxy": "^3.0.0",
"intersection-observer": "^0.5.1",
"jest-junit": "^3.6.0",
"lint-staged": "^7.2.0",
"mocha": "^3.1.2",
+3
View File
@@ -159,6 +159,9 @@ const config = {
'process.env': {
VERSION: `"${require('./package.json').version}"`,
NODE_ENV: `${JSON.stringify(process.env.NODE_ENV)}`,
TALK_DEFAULT_LAZY_RENDER: `${JSON.stringify(
process.env.TALK_DEFAULT_LAZY_RENDER
)}`,
},
}),
new webpack.EnvironmentPlugin({
+5
View File
@@ -6042,6 +6042,11 @@ interpret@^1.0.0, interpret@^1.0.1:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
intersection-observer@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.5.1.tgz#e340fc56ce74290fe2b2394d1ce88c4353ac6dfa"
integrity sha512-Zd7Plneq82kiXFixs7bX62YnuZ0BMRci9br7io88LwDyF3V43cQMI+G5IiTlTNTt+LsDUppl19J/M2Fp9UkH6g==
invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"