/**
* Toast maker similar to Android.
*
* @module Toasts
*/
import {Utilities, DOMTools, DiscordSelectors} from "modules";
import {Icons} from "ui";
import CSS from "../styles/toasts.css";
export default class Toast {
static get CSS() {return CSS;}
/** Shorthand for `type = "success"` for {@link module:Toasts.show} */
static async success(content, options = {}) {return this.show(content, Object.assign(options, {type: "success"}));}
/** Shorthand for `type = "info"` for {@link module:Toasts.show} */
static async info(content, options = {}) {return this.show(content, Object.assign(options, {type: "info"}));}
/** Shorthand for `type = "warning"` for {@link module:Toasts.show} */
static async warning(content, options = {}) {return this.show(content, Object.assign(options, {type: "warning"}));}
/** Shorthand for `type = "error"` for {@link module:Toasts.show} */
static async error(content, options = {}) {return this.show(content, Object.assign(options, {type: "error"}));}
/** Shorthand for `type = "default"` for {@link module:Toasts.show} */
static async default(content, options = {}) {return this.show(content, Object.assign(options, {type: "default"}));}
/**
* Shows a simple toast, similar to Android, centered over
* the textarea if it exists, and center screen otherwise.
* Vertically it shows towards the bottom like in Android.
* @param {string} content - The string to show in the toast.
* @param {object} options - additional options for the toast
* @param {string} [options.type] - Changes the type of the toast stylistically and semantically. {@link module:Toasts.ToastTypes}
* @param {string} [options.icon] - URL to an optional icon
* @param {number} [options.timeout=3000] - Adjusts the time (in ms) the toast should be shown for before disappearing automatically
* @returns {Promise} - Promise that resolves when the toast is removed from the DOM
*/
static async show(content, options = {}) {
const {type = "", icon = "", timeout = 3000} = options;
this.ensureContainer();
const toast = DOMTools.parseHTML(this.buildToast(content, this.parseType(type), icon));
document.querySelector(".toasts").appendChild(toast);
await new Promise(resolve => setTimeout(resolve, timeout));
toast.classList.add("closing");
await new Promise(resolve => setTimeout(resolve, 300));
toast.remove();
if (!document.querySelectorAll(".toasts .toast").length) document.querySelector(".toasts").remove();
}
static buildToast(message, type, icon) {
const hasIcon = type || icon;
const className = `toast ${hasIcon ? "toast-has-icon" : ""} ${type && type != "default" ? `toast-${type}` : ""}`;
if (!icon && type) icon = type;
return Utilities.formatString(`<div class="{{className}}">{{icon}}<div class="toast-text">{{message}}</div></div>`, {
className: className,
icon: hasIcon ? this.getIcon(icon) : "",
message: message
});
}
static getIcon(icon) {
let iconInner = `<img src="${icon}" width="20" height="20" />`;
switch (icon) {
case "success": iconInner = Icons.IconSuccess(20); break; // eslint-disable-line new-cap
case "warning": iconInner = Icons.IconWarning(20); break; // eslint-disable-line new-cap
case "info": iconInner = Icons.IconInfo(20); break; // eslint-disable-line new-cap
case "error": iconInner = Icons.IconError(20); // eslint-disable-line new-cap
}
return Utilities.formatString(`<div class="toast-icon">{{icon}}</div>`, {icon: iconInner});
}
static ensureContainer() {
if (document.querySelector(".toasts")) return;
const channelClass = DiscordSelectors.ChannelList.sidebar;
const container = channelClass ? document.querySelector(`${channelClass} ~ div:not([style])`) : null;
const memberlist = container ? container.querySelector(DiscordSelectors.MemberList.membersWrap) : null;
const form = container ? container.querySelector("form") : null;
const left = container ? container.getBoundingClientRect().left : 310;
const right = memberlist ? memberlist.getBoundingClientRect().left : 0;
const width = right ? right - container.getBoundingClientRect().left : container.offsetWidth;
const bottom = form ? form.offsetHeight : 80;
const toastWrapper = document.createElement("div");
toastWrapper.classList.add("toasts");
toastWrapper.style.setProperty("left", left + "px");
toastWrapper.style.setProperty("width", width + "px");
toastWrapper.style.setProperty("bottom", bottom + "px");
document.querySelector("#app-mount").appendChild(toastWrapper);
}
static parseType(type) {
return this.ToastTypes.hasOwnProperty(type) ? this.ToastTypes[type] : "";
}
/**
* Enumeration of accepted types.
*/
static get ToastTypes() {
return {
"default": "",
"error": "error",
"success": "success",
"warning": "warning",
"info": "info"
};
}
}