mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 07:56:39 +08:00
196 lines
5.2 KiB
JavaScript
196 lines
5.2 KiB
JavaScript
import queryString from 'query-string';
|
|
import pym from 'pym.js';
|
|
import EventEmitter from 'eventemitter2';
|
|
import { buildUrl } from 'coral-framework/utils/url';
|
|
import Snackbar from './Snackbar';
|
|
import {
|
|
createStorage,
|
|
connectStorageToPym,
|
|
} from 'coral-framework/services/storage';
|
|
|
|
const NOTIFICATION_OFFSET = 200;
|
|
|
|
// Build the URL to load in the pym iframe.
|
|
function buildStreamIframeUrl(talkBaseUrl, query) {
|
|
let url = [
|
|
talkBaseUrl,
|
|
talkBaseUrl.match(/\/$/) ? '' : '/', // make sure no double-'/' if opts.talk already ends with '/'
|
|
'embed/stream?',
|
|
].join('');
|
|
|
|
url += queryString.stringify(query);
|
|
|
|
return url;
|
|
}
|
|
|
|
// Get dimensions of viewport.
|
|
function viewportDimensions() {
|
|
let e = window,
|
|
a = 'inner';
|
|
if (!('innerWidth' in window)) {
|
|
a = 'client';
|
|
e = document.documentElement || document.body;
|
|
}
|
|
|
|
return {
|
|
width: e[`${a}Width`],
|
|
height: e[`${a}Height`],
|
|
};
|
|
}
|
|
|
|
export default class Stream {
|
|
constructor(el, talkBaseUrl, query, config) {
|
|
this.query = query;
|
|
|
|
// Extract the non-opts opts from the object.
|
|
const {
|
|
events = null,
|
|
snackBarStyles = null,
|
|
onAuthChanged = null,
|
|
...opts
|
|
} = config;
|
|
|
|
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 || {});
|
|
|
|
// Workaround: IOS Safari ignores `width` but respects `min-width` value.
|
|
this.pym.el.firstChild.style.width = '1px';
|
|
this.pym.el.firstChild.style.minWidth = '100%';
|
|
|
|
// Resize parent iframe height when child height changes
|
|
let cachedHeight;
|
|
this.pym.onMessage('height', height => {
|
|
if (height !== cachedHeight) {
|
|
this.pym.el.firstChild.style.height = `${height}px`;
|
|
cachedHeight = height;
|
|
}
|
|
});
|
|
|
|
// 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));
|
|
});
|
|
|
|
// If the auth changes, and someone is listening for it, then re-emit it.
|
|
if (onAuthChanged) {
|
|
this.pym.onMessage('coral-auth-changed', message => {
|
|
onAuthChanged(message ? JSON.parse(message) : null);
|
|
});
|
|
}
|
|
|
|
// Attach the snackbar to the pym parent and to the body of the page.
|
|
this.snackBar.attach(window.document.body, this.pym);
|
|
|
|
// Remove the permalink comment id from the hash.
|
|
this.pym.onMessage('coral-view-all-comments', () => {
|
|
const search = queryString.stringify({
|
|
...queryString.parse(location.search),
|
|
commentId: undefined,
|
|
});
|
|
|
|
// Remove the commentId url param.
|
|
const url = buildUrl({ ...location, search });
|
|
|
|
// Change the url.
|
|
window.history.replaceState({}, document.title, url);
|
|
});
|
|
|
|
// Remove the permalink comment id from the hash.
|
|
this.pym.onMessage('coral-view-comment', id => {
|
|
const search = queryString.stringify({
|
|
...queryString.parse(location.search),
|
|
commentId: id,
|
|
});
|
|
|
|
// Remove the commentId url param.
|
|
const url = buildUrl({ ...location, search });
|
|
|
|
// Change the url.
|
|
window.history.replaceState({}, document.title, url);
|
|
});
|
|
|
|
// Helps child show notifications at the right scrollTop.
|
|
this.pym.onMessage('getPosition', () => {
|
|
const { height } = viewportDimensions();
|
|
let position = height + document.body.scrollTop;
|
|
|
|
if (position > NOTIFICATION_OFFSET) {
|
|
position = position - NOTIFICATION_OFFSET;
|
|
}
|
|
|
|
this.pym.sendMessage('position', position);
|
|
});
|
|
|
|
// When end-user clicks link in iframe, open it in parent context
|
|
this.pym.onMessage('navigate', url => {
|
|
window.open(url, '_blank').focus();
|
|
});
|
|
|
|
// Pass events from iframe to the event emitter.
|
|
this.pym.onMessage('event', raw => {
|
|
const { eventName, value } = JSON.parse(raw);
|
|
this.emitter.emit(eventName, value);
|
|
});
|
|
|
|
// If the user clicks outside the embed, then tell the embed.
|
|
document.addEventListener('click', this.handleClick.bind(this), true);
|
|
|
|
// Listens to local storage requests on pym and relay it to local storage.
|
|
connectStorageToPym(
|
|
createStorage('localStorage'),
|
|
this.pym,
|
|
'localStorage'
|
|
);
|
|
|
|
// Listens to session storage requests on pym and relay it to session storage.
|
|
connectStorageToPym(
|
|
createStorage('sessionStorage'),
|
|
this.pym,
|
|
'sessionStorage'
|
|
);
|
|
}
|
|
|
|
enablePluginsDebug() {
|
|
this.pym.sendMessage('enablePluginsDebug');
|
|
}
|
|
|
|
disablePluginsDebug() {
|
|
this.pym.sendMessage('disablePluginsDebug');
|
|
}
|
|
|
|
login(token) {
|
|
this.pym.sendMessage('login', token);
|
|
}
|
|
|
|
logout() {
|
|
this.pym.sendMessage('logout');
|
|
}
|
|
|
|
remove() {
|
|
// Remove the event listeners.
|
|
document.removeEventListener('click', this.handleClick.bind(this));
|
|
this.emitter.removeAllListeners();
|
|
|
|
// Remove the snackbar.
|
|
this.snackBar.remove();
|
|
|
|
// Remove the pym parent.
|
|
this.pym.remove();
|
|
}
|
|
|
|
handleClick() {
|
|
this.pym.sendMessage('click');
|
|
}
|
|
}
|