You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vizality/renderer/src/api/Popups.js

151 lines
4.5 KiB

/**
* The popups API is used for .
* @module Popups
* @memberof API
* @namespace API.Popups
* @version 1.0.0
*/
import { PopupWindow, Titlebar, Spinner } from '@vizality/components';
import React, { memo, useState, useEffect } from 'react';
import { getModule } from '@vizality/webpack';
import { Events } from '@vizality/constants';
import { API } from '@vizality/entities';
/**
* @extends API
* @extends Events
*/
export default class Popups extends API {
/**
* Shuts down the API, removing all listeners and stored objects.
*/
stop () {
this.unregisterAllPopups();
this.removeAllListeners();
delete vizality.api.popups;
}
/**
* Opens a website URL in an iframe in a popup popup.
* @param {object} popup Popup
* @param {object} [options={}] Popup options
* @emits Popups#popupOpen
*/
async openPopup (popup, options = {}) {
try {
popup.popupId = popup.popupId || `popup-${(Math.random().toString(36) + Date.now()).substring(2, 10)}`;
options.width = options.width || 800;
options.height = options.height || 600;
let Render;
// Show a titlebar if they don't specify titlebar={false}
if (typeof popup.titlebar !== 'boolean') popup.titlebar = true;
popup.title = popup.title || 'Discord Popup';
// Let url override render
if (popup.url) popup.render = null;
if (popup.render) Render = popup.render;
// Center the popup based on the user's screen size
const y = window.top.outerHeight / 2 + window.top.screenY - (options.height / 2);
const x = window.top.outerWidth / 2 + window.top.screenX - (options.width / 2);
options.top = options.top || y;
options.left = options.left || x;
if (window.popouts.has(popup.popupId)) {
throw new Error(`Popup with ID "${popup.popupId}" already active!`);
}
const PopupContent = memo(props => {
const [ loading, setLoading ] = useState(true);
const { url, windowKey, titlebarType, titlebar, children } = props;
const sandbox = 'allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts';
useEffect(() => {
this.emit(Events.VIZALITY_POPUP_OPEN, windowKey);
return () => {
this.emit(Events.VIZALITY_POPUP_CLOSE, windowKey);
window.popouts.delete(windowKey);
};
}, []);
return (
<div className='vz-popup' vz-titlebar={titlebar ? '' : null}>
{titlebar && <Titlebar type={titlebarType} windowKey={windowKey} focused={true} />}
{loading && url && <Spinner className='vz-popup-spinner' />}
<div className='vz-popup-content-wrapper'>
{url
? <iframe
src={url}
onLoad={() => setLoading(false)}
className='vz-popup-content'
allowtransparency={true}
sandbox={sandbox}
/>
: children
}
</div>
</div>
);
});
return new Promise(() => {
const popupModule = getModule('setAlwaysOnTop', 'open');
popupModule.open(`DISCORD_${popup.popupId}`, key => {
return (
<PopupWindow windowKey={key} {...popup}>
<PopupContent windowKey={key} {...popup}>
{popup.render && !popup.url && <Render {...popup} />}
</PopupContent>
</PopupWindow>
);
}, options);
});
} catch (err) {
return this.error(err);
}
}
/**
* Closes a popup.
* @param {string} popupId Popup ID
* @emits Popups#popupClosed
*/
closePopup (popupId) {
try {
if (!window.popouts.has(popupId)) {
throw new Error(`Popup with ID "${popupId}" not found!`);
}
const popupModule = getModule('setAlwaysOnTop', 'open');
popupModule.close(`DISCORD_${popupId}`);
} catch (err) {
return this.error(err);
}
}
/**
* Sets a popup to always be on top of other popups.
* @param {string} popupId Popup ID
* @param {boolean} [alwaysOnTop=true] Whether the popup should always be on top or not
*/
setAlwaysOnTop (popupId, alwaysOnTop = true) {
try {
const popupModule = getModule('setAlwaysOnTop', 'open');
popupModule.setAlwaysOnTop(`DISCORD_${popupId}`, alwaysOnTop);
} catch (err) {
return this.error(err);
}
}
stop () {
this.closeAllPopups();
this.removeAllListeners();
delete vizality.api.popups;
}
}