mirror of
https://github.com/wassname/talk.git
synced 2026-07-05 13:45:40 +08:00
168 lines
4.6 KiB
JavaScript
168 lines
4.6 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, opts) {
|
|
|
|
// Create and save the options.
|
|
|
|
this.opts = opts;
|
|
this.query = query;
|
|
|
|
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(opts.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 (opts.events) {
|
|
opts.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 (opts.onAuthChanged) {
|
|
this.pym.onMessage('coral-auth-changed', (message) => {
|
|
opts.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 storage requests on pym and relay it to local storage.
|
|
connectStorageToPym(createStorage(), this.pym);
|
|
}
|
|
|
|
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');
|
|
}
|
|
}
|