start to convert emotemenu

This commit is contained in:
Zack Rauen 2019-05-31 01:53:11 -04:00
parent e06d2d9d9e
commit b64f35fa88
29 changed files with 1502 additions and 1234 deletions

View File

@ -19,7 +19,7 @@
"semi": 2,
"space-infix-ops": ["error", {"int32Hint": false}],
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
"no-console": 0,
"no-console": 2,
"brace-style": ["error", "stroustrup", {"allowSingleLine": true}],
"keyword-spacing": 2,
"no-else-return": 2,

View File

@ -308,7 +308,7 @@
#bda-qem-twitch-container, #bda-qem-favourite-container {
width: 346px;
height: 327px;
height: 329px;
background-color: #FFF;
border-radius: 0 0 5px 5px;
}
@ -739,12 +739,12 @@ body .ace_closeButton {
background: none;
border: none;
}
body .ace_closeButton::before {
content: "✖";
color: #36393f;
}
body .ace_closeButton:active {
transform: translateY(2px);
}
@ -1780,11 +1780,11 @@ body .ace_closeButton:active {
border-color: #202020;
}
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar,
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar-track,
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar,
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar-track,
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar-track-piece,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar-track,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar-track,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar-track-piece,
.bda-dark .emojiPicker-3m1S-j .scroller-3vODG7::-webkit-scrollbar,
.bda-dark .emojiPicker-3m1S-j .scroller-3vODG7::-webkit-scrollbar-track,
@ -1793,7 +1793,7 @@ body .ace_closeButton:active {
border-color: #303030 !important;
}
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar-thumb,
.bda-dark #bda-qem-twitch-container .scroller::-webkit-scrollbar-thumb,
.bda-dark #bda-qem-favourite-container .scroller::-webkit-scrollbar-thumb,
.bda-dark .emojiPicker-3g68GS .scroller-3vODG7::-webkit-scrollbar-thumb {
border-color: #202020 !important;

1454
js/main.js

File diff suppressed because it is too large Load Diff

2
js/main.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -5,4 +5,6 @@ export {default as PublicServers} from "./publicservers";
export {default as DarkMode} from "./darkmode";
export {default as MinimalMode} from "./minimalmode";
export {default as TwentyFourHour} from "./24hour";
export {default as ColoredText} from "./coloredtext";
export {default as ColoredText} from "./coloredtext";
export {default as VoiceDisconnect} from "./voicedisconnect";
export {default as EmoteMenu} from "./emotemenu";

204
src/builtins/emotemenu.js Normal file
View File

@ -0,0 +1,204 @@
import Builtin, {onSettingChange} from "../structs/builtin";
import {SettingsCookie, Emotes} from "data";
import {DataStore, Utilities, Events} from "modules";
const headerHTML = `<div id="bda-qem">
<button class="active" id="bda-qem-twitch">Twitch</button>
<button id="bda-qem-favourite">Favourite</button>
<button id="bda-qem-emojis">Emojis</buttond>
</div>`;
const twitchEmoteHTML = `<div id="bda-qem-twitch-container">
<div class="scroller-wrap scrollerWrap-2lJEkd fade">
<div class="scroller scroller-2FKFPG">
<div class="emote-menu-inner">
</div>
</div>
</div>
</div>`;
const favoritesHTML = `<div id="bda-qem-favourite-container">
<div class="scroller-wrap scrollerWrap-2lJEkd fade">
<div class="scroller scroller-2FKFPG">
<div class="emote-menu-inner">
</div>
</div>
</div>
</div>`;
const makeEmote = (emote, url, options = {}) => {
const {onContextMenu, onClick} = options;
const emoteContainer = $(`<div class="emote-container">
<img class="emote-icon" alt="${emote}" src="${url}" title="${emote}">
</div>`)[0];
if (onContextMenu) emoteContainer.addEventListener("contextmenu", onContextMenu);
emoteContainer.addEventListener("click", onClick);
return emoteContainer;
};
export default new class EmoteMenu extends Builtin {
get name() {return "EmoteMenu";}
get category() {return "Modules";}
get id() {return "bda-es-0";}
get hideEmojisID() {return "bda-es-9";}
get hideEmojis() {return SettingsCookie[this.hideEmojisID];}
constructor() {
super();
this.lastTab = "bda-qem-emojis";
this.favoriteEmotes = {};
this.qmeHeader = $(headerHTML)[0];
for (const button of this.qmeHeader.getElementsByTagName("button")) button.addEventListener("click", this.switchMenu.bind(this));
this.teContainer = $(twitchEmoteHTML)[0];
this.teContainerInner = this.teContainer.querySelector(".emote-menu-inner");
this.faContainer = $(favoritesHTML)[0];
this.faContainerInner = this.faContainer.querySelector(".emote-menu-inner");
this.observer = new MutationObserver(mutations => {for (const mutation of mutations) this.observe(mutation);});
this.enableHideEmojis = this.enableHideEmojis.bind(this);
this.disableHideEmojis = this.disableHideEmojis.bind(this);
}
initialize() {
super.initialize();
const fe = DataStore.getBDData("bdfavemotes");
if (fe !== "" && fe !== null) this.favoriteEmotes = JSON.parse(atob(fe));
this.updateFavorites();
}
async enabled() {
await new Promise(resolve => {
Events.on("emotes-loaded", resolve);
});
this.updateTwitchEmotes();
this.log("Starting to observe");
this.observer.observe(document.getElementById("app-mount"), {
childList: true,
subtree: true
});
this.hideEmojiCancel = onSettingChange(this.category, this.hideEmojisID, this.enableHideEmojis, this.disableHideEmojis);
if (this.hideEmojis) this.enableHideEmojis();
}
disabled() {
this.observer.disconnect();
this.disableHideEmojis();
if (this.hideEmojiCancel) this.hideEmojiCancel();
}
enableHideEmojis() {
$(".emojiPicker-3m1S-j").addClass("bda-qme-hidden");
}
disableHideEmojis() {
$(".emojiPicker-3m1S-j").removeClass("bda-qme-hidden");
}
insertEmote(emote) {
const ta = Utilities.getTextArea();
Utilities.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote);
}
favContext(e) {
e.stopPropagation();
const em = e.target.closest(".emote-container").children[0];
const menu = $(`<div id="removemenu" class="bd-context-menu context-menu theme-dark">Remove</div>`);
menu.css({
top: e.pageY - $("#bda-qem-favourite-container").offset().top,
left: e.pageX - $("#bda-qem-favourite-container").offset().left
});
$(em).parent().append(menu);
menu.on("click", (e) => {
e.preventDefault();
e.stopPropagation();
$(em).remove();
delete this.favoriteEmotes[$(em).attr("title")];
this.updateFavorites();
$(document).off("mousedown.emotemenu");
});
$(document).on("mousedown.emotemenu", function(e) {
if (e.target.id == "removemenu") return;
$("#removemenu").remove();
$(document).off("mousedown.emotemenu");
});
}
switchMenu(e) {
let id = typeof(e) == "string" ? e : $(e.target).attr("id");
if (id == "bda-qem-emojis" && this.hideEmojis) id = "bda-qem-favourite";
const twitch = $("#bda-qem-twitch");
const fav = $("#bda-qem-favourite");
const emojis = $("#bda-qem-emojis");
twitch.removeClass("active");
fav.removeClass("active");
emojis.removeClass("active");
$(".emojiPicker-3m1S-j").hide();
$("#bda-qem-favourite-container").hide();
$("#bda-qem-twitch-container").hide();
switch (id) {
case "bda-qem-twitch":
twitch.addClass("active");
$("#bda-qem-twitch-container").show();
break;
case "bda-qem-favourite":
fav.addClass("active");
$("#bda-qem-favourite-container").show();
break;
case "bda-qem-emojis":
emojis.addClass("active");
$(".emojiPicker-3m1S-j").show();
$(".emojiPicker-3m1S-j input").focus();
break;
}
if (id) this.lastTab = id;
}
observe(mutation) {
if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) return;
const node = mutation.addedNodes[0];
if (!node.classList.contains("popout-3sVMXz") || node.classList.contains("popoutLeft-30WmrD") || !node.getElementsByClassName("emojiPicker-3m1S-j").length) return;
const e = $(node);
if (this.hideEmojis) e.addClass("bda-qme-hidden");
else e.removeClass("bda-qme-hidden");
e.prepend(this.qmeHeader);
e.append(this.teContainer);
e.append(this.faContainer);
this.switchMenu(this.lastTab);
}
favorite(name, url) {
if (!this.favoriteEmotes.hasOwnProperty(name)) this.favoriteEmotes[name] = url;
this.updateFavorites();
}
updateTwitchEmotes() {
while (this.teContainerInner.firstChild) this.teContainerInner.firstChild.remove();
for (const emote in Emotes.TwitchGlobal) {
if (!Emotes.TwitchGlobal.hasOwnProperty(emote)) continue;
const url = Emotes.TwitchGlobal[emote];
const emoteElement = makeEmote(emote, url, {onClick: this.insertEmote.bind(this, emote)});
this.teContainerInner.append(emoteElement);
}
}
updateFavorites() {
while (this.faContainerInner.firstChild) this.faContainerInner.firstChild.remove();
for (const emote in this.favoriteEmotes) {
const url = this.favoriteEmotes[emote];
const emoteElement = makeEmote(emote, url, {onClick: this.insertEmote.bind(this, emote), onContextMenu: this.favContext.bind(this)});
this.faContainerInner.append(emoteElement);
}
DataStore.setBDData("bdfavemotes", btoa(JSON.stringify(this.favoriteEmotes)));
}
};

View File

@ -43,7 +43,6 @@ export default new class PublicServers extends Builtin {
}
render() {
// BdApi.alert("Broken", "Sorry but the Public Servers modules is currently broken, I recommend disabling this feature for now.");
const root = this.root;
if (!root) {
console.log("FAILED TO LOCATE ROOT: .layers");

View File

@ -0,0 +1,25 @@
import Builtin from "../structs/builtin";
import {DiscordModules} from "modules";
export default new class DarkMode extends Builtin {
get name() {return "VoiceDisconnect";}
get category() {return "Modules";}
get id() {return "bda-dc-0";}
constructor() {
super();
this.beforeUnload = this.beforeUnload.bind(this);
}
enabled() {
window.addEventListener("beforeunload", this.beforeUnload);
}
disabled() {
window.removeEventListener("beforeunload", this.beforeUnload);
}
beforeUnload() {
DiscordModules.ChannelActions.selectVoiceChannel(null, null);
}
};

View File

@ -1,4 +1,4 @@
import {SettingsCookie, SettingsInfo, PluginCookie, ThemeCookie, Plugins, Themes, Emotes, EmoteBlacklist} from "data";
import {SettingsCookie, SettingsInfo, Config, PluginCookie, ThemeCookie, Plugins, Themes, Emotes, EmoteBlacklist} from "data";
import proxyLocalStorage from "./localstorage";
import Core from "./modules/core";
import BdApi from "./modules/pluginapi";
@ -27,7 +27,10 @@ window.bdplugins = Plugins;
window.bdEmotes = Emotes;
window.bemotes = EmoteBlacklist;
window.bdPluginStorage = bdPluginStorage;
window.BDEvents = Events;
window.bdConfig = Config;
export default class CoreWrapper {
constructor(config) {

View File

@ -85,4 +85,12 @@ export default new class {
}});
}
};
};
// lc = WebpackModules.getByDisplayName("FluxContainer(Layers)")
// Patcher.after(lc.prototype, "render", (t,a,r) => {console.log(t,a,r);})
// return.type
// Patcher.after(temp3.prototype, "renderLayers", (t,a,r) => {
// console.log(t,a,r);
// if (t.props.layers.includes("USER_SETTINGS")) r[1].props.className = "user-settings-prop";
// })

View File

@ -2,15 +2,13 @@ import BDV2 from "./bdv2";
import Utilities from "./utilities";
import {Config, SettingsCookie} from "data";
import EmoteModule from "./emotes";
import QuickEmoteMenu from "./emotemenu";
// import VoiceMode from "./voicemode";
// import DevMode from "./devmode";
// import QuickEmoteMenu from "../builtins/emotemenu";
import PluginManager from "./pluginmanager";
import ThemeManager from "./thememanager";
import DataStore from "./datastore";
// import PublicServers from "./publicservers";
import SettingsPanel from "./settingspanel";
import * as Builtins from "builtins";
import {Modals} from "ui";
import Events from "./emitter";
function Core() {
}
@ -21,25 +19,26 @@ Core.prototype.setConfig = function(config) {
Core.prototype.init = async function() {
if (Config.version < Config.minSupportedVersion) {
this.alert("Not Supported", "BetterDiscord v" + Config.version + " (your version)" + " is not supported by the latest js (" + Config.bbdVersion + ").<br><br> Please download the latest version from <a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>GitHub</a>");
Modals.alert("Not Supported", "BetterDiscord v" + Config.version + " (your version)" + " is not supported by the latest js (" + Config.bbdVersion + ").<br><br> Please download the latest version from <a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>GitHub</a>");
return;
}
const latestLocalVersion = Config.updater ? Config.updater.LatestVersion : Config.latestVersion;
if (latestLocalVersion > Config.version) {
this.alert("Update Available", `
Modals.alert("Update Available", `
An update for BandagedBD is available (${latestLocalVersion})! Please Reinstall!<br /><br />
<a href='https://github.com/rauenzi/BetterDiscordApp/releases/latest' target='_blank'>Download Installer</a>
`);
}
Utilities.log("Startup", "Initializing Settings");
this.initSettings();
SettingsPanel.initialize();
Utilities.log("Startup", "Initializing EmoteModule");
window.emotePromise = EmoteModule.init().then(() => {
EmoteModule.initialized = true;
Utilities.log("Startup", "Initializing QuickEmoteMenu");
QuickEmoteMenu.init();
Events.dispatch("emotes-loaded");
// QuickEmoteMenu.init();
});
this.injectExternals();
@ -58,10 +57,6 @@ Core.prototype.init = async function() {
$("#customcss").detach().appendTo(document.head);
window.addEventListener("beforeunload", function() {
if (SettingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click();
});
// PublicServers.initialize();
EmoteModule.autoCapitalize();
@ -73,13 +68,8 @@ Core.prototype.init = async function() {
// Show loading errors
if (SettingsCookie["fork-ps-1"]) {
Utilities.log("Startup", "Collecting Startup Errors");
this.showContentErrors({plugins: pluginErrors, themes: themeErrors});
Modals.showContentErrors({plugins: pluginErrors, themes: themeErrors});
}
// if (!DataStore.getBDData(bbdVersion)) {
// BdApi.alert("BBD Updated!", ["Lots of things were fixed in this update like Public Servers, Minimal Mode, Dark Mode and 24 Hour Timestamps.", BdApi.React.createElement("br"), BdApi.React.createElement("br"), "Feel free to test them all out!"]);
// DataStore.setBDData(bbdVersion, true);
// }
};
Core.prototype.checkForGuilds = function() {
@ -102,26 +92,6 @@ Core.prototype.injectExternals = async function() {
if (window.require.original) window.require = window.require.original;
};
Core.prototype.initSettings = function () {
DataStore.initialize();
if (!DataStore.getSettingGroup("settings")) return this.saveSettings();
const savedSettings = this.loadSettings();
$("<style id=\"customcss\">").text(atob(DataStore.getBDData("bdcustomcss"))).appendTo(document.head);
for (const setting in savedSettings) {
if (savedSettings[setting] !== undefined) SettingsCookie[setting] = savedSettings[setting];
}
this.saveSettings();
};
Core.prototype.saveSettings = function () {
DataStore.setSettingGroup("settings", SettingsCookie);
};
Core.prototype.loadSettings = function () {
return DataStore.getSettingGroup("settings");
};
Core.prototype.initObserver = function () {
const mainObserver = new MutationObserver((mutations) => {
@ -143,10 +113,6 @@ Core.prototype.initObserver = function () {
if (!document.getElementById("bd-settings-sidebar")) SettingsPanel.renderSidebar();
}
}
// Emoji Picker
if (node.classList.contains("popout-3sVMXz") && !node.classList.contains("popoutLeft-30WmrD") && node.getElementsByClassName("emojiPicker-3m1S-j").length) QuickEmoteMenu.obsCallback(node);
}
});
@ -156,147 +122,5 @@ Core.prototype.initObserver = function () {
});
};
Core.prototype.alert = function(title, content) {
const modal = $(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
<div class="header header-1R_AjF">
<div class="title">${title}</div>
</div>
<div class="bd-modal-body">
<div class="scroller-wrap fade">
<div class="scroller">
${content}
</div>
</div>
</div>
<div class="footer footer-2yfCgX">
<button type="button">Okay</button>
</div>
</div>
</div>
</div>`);
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
};
Core.prototype.showContentErrors = function({plugins: pluginErrors = [], themes: themeErrors = []}) {
if (!pluginErrors || !themeErrors) return;
if (!pluginErrors.length && !themeErrors.length) return;
const modal = $(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal bd-content-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
<div class="header header-1R_AjF"><div class="title">Content Errors</div></div>
<div class="bd-modal-body">
<div class="tab-bar-container">
<div class="tab-bar TOP">
<div class="tab-bar-item">Plugins</div>
<div class="tab-bar-item">Themes</div>
</div>
</div>
<div class="table-header">
<div class="table-column column-name">Name</div>
<div class="table-column column-message">Message</div>
<div class="table-column column-error">Error</div>
</div>
<div class="scroller-wrap fade">
<div class="scroller">
</div>
</div>
</div>
<div class="footer footer-2yfCgX">
<button type="button">Okay</button>
</div>
</div>
</div>
</div>`);
function generateTab(errors) {
const container = $(`<div class="errors">`);
for (const err of errors) {
const error = $(`<div class="error">
<div class="table-column column-name">${err.name ? err.name : err.file}</div>
<div class="table-column column-message">${err.message}</div>
<div class="table-column column-error"><a class="error-link" href="">${err.error ? err.error.message : ""}</a></div>
</div>`);
container.append(error);
if (err.error) {
error.find("a").on("click", (e) => {
e.preventDefault();
Utilities.err("ContentManager", `Error details for ${err.name ? err.name : err.file}.`, err.error);
});
}
}
return container;
}
const tabs = [generateTab(pluginErrors), generateTab(themeErrors)];
modal.find(".tab-bar-item").on("click", (e) => {
e.preventDefault();
modal.find(".tab-bar-item").removeClass("selected");
$(e.target).addClass("selected");
modal.find(".scroller").empty().append(tabs[$(e.target).index()]);
});
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
if (pluginErrors.length) modal.find(".tab-bar-item")[0].click();
else modal.find(".tab-bar-item")[1].click();
};
/**
* This shows a toast similar to android towards the bottom of the screen.
*
* @param {string} content The string to show in the toast.
* @param {object} options Options object. Optional parameter.
* @param {string} options.type Changes the type of the toast stylistically and semantically. Choices: "", "info", "success", "danger"/"error", "warning"/"warn". Default: ""
* @param {boolean} options.icon Determines whether the icon should show corresponding to the type. A toast without type will always have no icon. Default: true
* @param {number} options.timeout Adjusts the time (in ms) the toast should be shown for before disappearing automatically. Default: 3000
*/
Core.prototype.showToast = function(content, options = {}) {
if (!Config.deferLoaded) return;
if (!document.querySelector(".bd-toasts")) {
const toastWrapper = document.createElement("div");
toastWrapper.classList.add("bd-toasts");
const boundingElement = document.querySelector(".chat-3bRxxu form, #friends, .noChannel-Z1DQK7, .activityFeed-28jde9");
toastWrapper.style.setProperty("left", boundingElement ? boundingElement.getBoundingClientRect().left + "px" : "0px");
toastWrapper.style.setProperty("width", boundingElement ? boundingElement.offsetWidth + "px" : "100%");
toastWrapper.style.setProperty("bottom", (document.querySelector(".chat-3bRxxu form") ? document.querySelector(".chat-3bRxxu form").offsetHeight : 80) + "px");
document.querySelector(".app, .app-2rEoOp").appendChild(toastWrapper);
}
const {type = "", icon = true, timeout = 3000} = options;
const toastElem = document.createElement("div");
toastElem.classList.add("bd-toast");
if (type) toastElem.classList.add("toast-" + type);
if (type && icon) toastElem.classList.add("icon");
toastElem.innerText = content;
document.querySelector(".bd-toasts").appendChild(toastElem);
setTimeout(() => {
toastElem.classList.add("closing");
setTimeout(() => {
toastElem.remove();
if (!document.querySelectorAll(".bd-toasts .bd-toast").length) document.querySelector(".bd-toasts").remove();
}, 300);
}, timeout);
};
export default new Core();

View File

@ -1,5 +1,5 @@
import {Config} from "data";
import BdApi from "./pluginapi";
const fs = require("fs");
const path = require("path");
const releaseChannel = DiscordNative.globals.releaseChannel;
@ -11,20 +11,15 @@ export default new class DataStore {
}
initialize() {
try {
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4));
const data = __non_webpack_require__(this.BDFile);
if (data.hasOwnProperty("settings")) this.data = data;
if (!fs.existsSync(this.settingsFile)) return;
let settings = __non_webpack_require__(this.settingsFile);
fs.unlinkSync(this.settingsFile);
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
this.setBDData("settings", settings);
}
catch (err) {
BdApi.alert("Corrupt Storage", "The bd storage has somehow become corrupt. You may either try to salvage the file or delete it then reload.");
}
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4));
const data = __non_webpack_require__(this.BDFile);
if (data.hasOwnProperty("settings")) this.data = data;
if (!fs.existsSync(this.settingsFile)) return;
let settings = __non_webpack_require__(this.settingsFile);
fs.unlinkSync(this.settingsFile);
if (settings.hasOwnProperty("settings")) settings = Object.assign({stable: {}, canary: {}, ptb: {}}, {[releaseChannel]: settings});
else settings = Object.assign({stable: {}, canary: {}, ptb: {}}, settings);
this.setBDData("settings", settings);
}
get BDFile() {return this._BDFile || (this._BDFile = path.resolve(Config.dataPath, "bdstorage.json"));}

View File

@ -1,146 +0,0 @@
/**
* A large list of known and useful webpack modules internal to Discord.
* Click the source link down below to view more info. Otherwise, if you
* have the library installed or have a plugin using this library,
* do `Object.keys(ZLibrary.DiscordModules)` in console for a list of modules.
* @module DiscordModules
* @version 0.0.3
*/
import Utilities from "./utilities";
import WebpackModules from "./webpackmodules";
export default Utilities.memoizeObject({
get React() {return WebpackModules.getByProps("createElement", "cloneElement");},
get ReactDOM() {return WebpackModules.getByProps("render", "findDOMNode");},
get Flux() {return WebpackModules.getByProps("connectStores");},
get Events() {return WebpackModules.getByPrototypes("setMaxListeners", "emit");},
/* Guild Info, Stores, and Utilities */
get GuildStore() {return WebpackModules.getByProps("getGuild");},
get SortedGuildStore() {return WebpackModules.getByProps("getSortedGuilds");},
get SelectedGuildStore() {return WebpackModules.getByProps("getLastSelectedGuildId");},
get GuildSync() {return WebpackModules.getByProps("getSyncedGuilds");},
get GuildInfo() {return WebpackModules.getByProps("getAcronym");},
get GuildChannelsStore() {return WebpackModules.getByProps("getChannels", "getDefaultChannel");},
get GuildMemberStore() {return WebpackModules.getByProps("getMember");},
get MemberCountStore() {return WebpackModules.getByProps("getMemberCounts");},
get GuildEmojiStore() {return WebpackModules.getByProps("getEmojis");},
get GuildActions() {return WebpackModules.getByProps("markGuildAsRead");},
get GuildPermissions() {return WebpackModules.getByProps("getGuildPermissions");},
/* Channel Store & Actions */
get ChannelStore() {return WebpackModules.getByProps("getChannels", "getDMFromUserId");},
get SelectedChannelStore() {return WebpackModules.getByProps("getLastSelectedChannelId");},
get ChannelActions() {return WebpackModules.getByProps("selectChannel");},
get PrivateChannelActions() {return WebpackModules.getByProps("openPrivateChannel");},
get ChannelSelector() {return WebpackModules.getByProps("selectGuild", "selectChannel");},
/* Current User Info, State and Settings */
get UserInfoStore() {return WebpackModules.getByProps("getToken");},
get UserSettingsStore() {return WebpackModules.getByProps("guildPositions");},
get AccountManager() {return WebpackModules.getByProps("register", "login");},
get UserSettingsUpdater() {return WebpackModules.getByProps("updateRemoteSettings");},
get OnlineWatcher() {return WebpackModules.getByProps("isOnline");},
get CurrentUserIdle() {return WebpackModules.getByProps("getIdleTime");},
get RelationshipStore() {return WebpackModules.getByProps("isBlocked", "getFriendIDs");},
get RelationshipManager() {return WebpackModules.getByProps("addRelationship");},
get MentionStore() {return WebpackModules.getByProps("getMentions");},
/* User Stores and Utils */
get UserStore() {return WebpackModules.getByProps("getCurrentUser");},
get UserStatusStore() {return WebpackModules.getByProps("getStatus", "getState");},
get UserTypingStore() {return WebpackModules.getByProps("isTyping");},
get UserActivityStore() {return WebpackModules.getByProps("getActivity");},
get UserNameResolver() {return WebpackModules.getByProps("getName");},
get UserNoteStore() {return WebpackModules.getByProps("getNote");},
get UserNoteActions() {return WebpackModules.getByProps("updateNote");},
/* Emoji Store and Utils */
get EmojiInfo() {return WebpackModules.getByProps("isEmojiDisabled");},
get EmojiUtils() {return WebpackModules.getByProps("getGuildEmoji");},
get EmojiStore() {return WebpackModules.getByProps("getByCategory", "EMOJI_NAME_RE");},
/* Invite Store and Utils */
get InviteStore() {return WebpackModules.getByProps("getInvites");},
get InviteResolver() {return WebpackModules.getByProps("findInvite");},
get InviteActions() {return WebpackModules.getByProps("acceptInvite");},
/* Discord Objects & Utils */
get DiscordConstants() {return WebpackModules.getByProps("Permissions", "ActivityTypes", "StatusTypes");},
get DiscordPermissions() {return WebpackModules.getByProps("Permissions", "ActivityTypes", "StatusTypes").Permissions;},
get PermissionUtils() {return WebpackModules.getByProps("getHighestRole");},
get ColorConverter() {return WebpackModules.getByProps("hex2int");},
get ColorShader() {return WebpackModules.getByProps("darken");},
get TinyColor() {return WebpackModules.getByPrototypes("toRgb");},
get ClassResolver() {return WebpackModules.getByProps("getClass");},
get ButtonData() {return WebpackModules.getByProps("ButtonSizes");},
get IconNames() {return WebpackModules.getByProps("IconNames");},
get NavigationUtils() {return WebpackModules.getByProps("transitionTo", "replaceWith", "getHistory");},
/* Discord Messages */
get MessageStore() {return WebpackModules.getByProps("getMessages");},
get MessageActions() {return WebpackModules.getByProps("jumpToMessage", "_sendMessage");},
get MessageQueue() {return WebpackModules.getByProps("enqueue");},
get MessageParser() {return WebpackModules.getByProps("createMessage", "parse", "unparse");},
/* Text Processing */
get hljs() {return WebpackModules.getByProps("highlight", "highlightBlock");},
get SimpleMarkdown() {return WebpackModules.getByProps("parseBlock", "parseInline", "defaultOutput");},
/* Experiments */
get ExperimentStore() {return WebpackModules.getByProps("getExperimentOverrides");},
get ExperimentsManager() {return WebpackModules.getByProps("isDeveloper");},
get CurrentExperiment() {return WebpackModules.getByProps("getExperimentId");},
/* Images, Avatars and Utils */
get ImageResolver() {return WebpackModules.getByProps("getUserAvatarURL", "getGuildIconURL");},
get ImageUtils() {return WebpackModules.getByProps("getSizedImageSrc");},
get AvatarDefaults() {return WebpackModules.getByProps("getUserAvatarURL", "DEFAULT_AVATARS");},
/* Window, DOM, HTML */
get WindowInfo() {return WebpackModules.getByProps("isFocused", "windowSize");},
get TagInfo() {return WebpackModules.getByProps("VALID_TAG_NAMES");},
get DOMInfo() {return WebpackModules.getByProps("canUseDOM");},
/* Locale/Location and Time */
get LocaleManager() {return WebpackModules.getByProps("setLocale");},
get Moment() {return WebpackModules.getByProps("parseZone");},
get LocationManager() {return WebpackModules.getByProps("createLocation");},
get Timestamps() {return WebpackModules.getByProps("fromTimestamp");},
/* Strings and Utils */
get Strings() {return WebpackModules.getByProps("Messages").Messages;},
get StringFormats() {return WebpackModules.getByProps("a", "z");},
get StringUtils() {return WebpackModules.getByProps("toASCII");},
/* URLs and Utils */
get URLParser() {return WebpackModules.getByProps("Url", "parse");},
get ExtraURLs() {return WebpackModules.getByProps("getArticleURL");},
/* Drag & Drop */
get DNDActions() {return WebpackModules.getByProps("beginDrag");},
get DNDSources() {return WebpackModules.getByProps("addTarget");},
get DNDObjects() {return WebpackModules.getByProps("DragSource");},
/* Media Stuff (Audio/Video) */
get MediaDeviceInfo() {return WebpackModules.getByProps("Codecs", "SUPPORTED_BROWSERS");},
get MediaInfo() {return WebpackModules.getByProps("getOutputVolume");},
get MediaEngineInfo() {return WebpackModules.getByProps("MediaEngineFeatures");},
get VoiceInfo() {return WebpackModules.getByProps("EchoCancellation");},
get VideoStream() {return WebpackModules.getByProps("getVideoStream");},
get SoundModule() {return WebpackModules.getByProps("playSound");},
/* Electron & Other Internals with Utils*/
get ElectronModule() {return WebpackModules.getByProps("setBadge");},
get Dispatcher() {return WebpackModules.getByProps("dirtyDispatch");},
get PathUtils() {return WebpackModules.getByProps("hasBasename");},
get NotificationModule() {return WebpackModules.getByProps("showNotification");},
get RouterModule() {return WebpackModules.getByProps("Router");},
get APIModule() {return WebpackModules.getByProps("getAPIBaseURL");},
get AnalyticEvents() {return WebpackModules.getByProps("AnalyticEventConfigs");},
get KeyGenerator() {return WebpackModules.getByRegex(/"binary"/);},
get Buffers() {return WebpackModules.getByProps("Buffer", "kMaxLength");},
get DeviceStore() {return WebpackModules.getByProps("getDevices");},
get SoftwareInfo() {return WebpackModules.getByProps("os");},
get CurrentContext() {return WebpackModules.getByProps("setTagsContext");}
});

View File

@ -1,5 +1,9 @@
const EventEmitter = require("events");
export default new class BDEvents extends EventEmitter {
constructor() {
super();
this.setMaxListeners(20);
}
dispatch(eventName, ...args) {
this.emit(eventName, ...args);
}

View File

@ -1,185 +0,0 @@
import {SettingsCookie, Emotes} from "data";
import DataStore from "./datastore";
import Utilities from "./utilities";
function QuickEmoteMenu() {
}
QuickEmoteMenu.prototype.init = function() {
this.initialized = true;
$(document).on("mousedown", function(e) {
if (e.target.id != "rmenu") $("#rmenu").remove();
});
this.favoriteEmotes = {};
const fe = DataStore.getBDData("bdfavemotes");
if (fe !== "" && fe !== null) {
this.favoriteEmotes = JSON.parse(atob(fe));
}
let qmeHeader = "";
qmeHeader += "<div id=\"bda-qem\">";
qmeHeader += " <button class=\"active\" id=\"bda-qem-twitch\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Twitch</button>";
qmeHeader += " <button id=\"bda-qem-favourite\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Favourite</button>";
qmeHeader += " <button id=\"bda-qem-emojis\" onclick='quickEmoteMenu.switchHandler(this); return false;'>Emojis</buttond>";
qmeHeader += "</div>";
this.qmeHeader = qmeHeader;
let teContainer = "";
teContainer += "<div id=\"bda-qem-twitch-container\">";
teContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
teContainer += " <div class=\"scroller scroller-2FKFPG\">";
teContainer += " <div class=\"emote-menu-inner\">";
let url = "";
for (const emote in Emotes.TwitchGlobal) {
if (Emotes.TwitchGlobal.hasOwnProperty(emote)) {
url = Emotes.TwitchGlobal[emote];
teContainer += "<div class=\"emote-container\">";
teContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\">";
teContainer += " </img>";
teContainer += "</div>";
}
}
teContainer += " </div>";
teContainer += " </div>";
teContainer += " </div>";
teContainer += "</div>";
this.teContainer = teContainer;
let faContainer = "";
faContainer += "<div id=\"bda-qem-favourite-container\">";
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
faContainer += " <div class=\"emote-menu-inner\">";
for (const emote in this.favoriteEmotes) {
url = this.favoriteEmotes[emote];
faContainer += "<div class=\"emote-container\">";
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu='quickEmoteMenu.favContext(event, this);'>";
faContainer += " </img>";
faContainer += "</div>";
}
faContainer += " </div>";
faContainer += " </div>";
faContainer += " </div>";
faContainer += "</div>";
this.faContainer = faContainer;
};
QuickEmoteMenu.prototype.favContext = function(e, em) {
e.stopPropagation();
const menu = $("<div>", {"id": "removemenu", "data-emoteid": $(em).prop("title"), "text": "Remove", "class": "bd-context-menu context-menu theme-dark"});
menu.css({
top: e.pageY - $("#bda-qem-favourite-container").offset().top,
left: e.pageX - $("#bda-qem-favourite-container").offset().left
});
$(em).parent().append(menu);
menu.on("click", function(e) {
e.preventDefault();
e.stopPropagation();
$(this).remove();
delete this.favoriteEmotes[$(this).data("emoteid")];
this.updateFavorites();
return false;
});
return false;
};
QuickEmoteMenu.prototype.switchHandler = function(e) {
this.switchQem($(e).attr("id"));
};
QuickEmoteMenu.prototype.switchQem = function(id) {
const twitch = $("#bda-qem-twitch");
const fav = $("#bda-qem-favourite");
const emojis = $("#bda-qem-emojis");
twitch.removeClass("active");
fav.removeClass("active");
emojis.removeClass("active");
$(".emojiPicker-3m1S-j").hide();
$("#bda-qem-favourite-container").hide();
$("#bda-qem-twitch-container").hide();
switch (id) {
case "bda-qem-twitch":
twitch.addClass("active");
$("#bda-qem-twitch-container").show();
break;
case "bda-qem-favourite":
fav.addClass("active");
$("#bda-qem-favourite-container").show();
break;
case "bda-qem-emojis":
emojis.addClass("active");
$(".emojiPicker-3m1S-j").show();
$(".emojiPicker-3m1S-j .search-bar-inner input, .emojiPicker-3m1S-j .search-bar-inner input").focus();
break;
}
this.lastTab = id;
const emoteIcon = $(".emote-icon");
emoteIcon.off();
emoteIcon.on("click", function () {
const emote = $(this).attr("title");
const ta = Utilities.getTextArea();
Utilities.insertText(ta[0], ta.val().slice(-1) == " " ? ta.val() + emote : ta.val() + " " + emote);
});
};
QuickEmoteMenu.prototype.obsCallback = function (elem) {
if (!this.initialized) return;
const e = $(elem);
if (!SettingsCookie["bda-es-9"]) {
e.addClass("bda-qme-hidden");
}
else {
e.removeClass("bda-qme-hidden");
}
if (!SettingsCookie["bda-es-0"]) return;
e.prepend(this.qmeHeader);
e.append(this.teContainer);
e.append(this.faContainer);
if (this.lastTab == undefined) {
this.lastTab = "bda-qem-emojis";
}
this.switchQem(this.lastTab);
};
QuickEmoteMenu.prototype.favorite = function (name, url) {
if (!this.favoriteEmotes.hasOwnProperty(name)) {
this.favoriteEmotes[name] = url;
}
this.updateFavorites();
};
QuickEmoteMenu.prototype.updateFavorites = function () {
let faContainer = "";
faContainer += "<div id=\"bda-qem-favourite-container\">";
faContainer += " <div class=\"scroller-wrap scrollerWrap-2lJEkd fade\">";
faContainer += " <div class=\"scroller scroller-2FKFPG\">";
faContainer += " <div class=\"emote-menu-inner\">";
for (const emote in this.favoriteEmotes) {
const url = this.favoriteEmotes[emote];
faContainer += "<div class=\"emote-container\">";
faContainer += " <img class=\"emote-icon\" alt=\"\" src=\"" + url + "\" title=\"" + emote + "\" oncontextmenu=\"quickEmoteMenu.favContext(event, this);\">";
faContainer += " </img>";
faContainer += "</div>";
}
faContainer += " </div>";
faContainer += " </div>";
faContainer += " </div>";
faContainer += "</div>";
this.faContainer = faContainer;
$("#bda-qem-favourite-container").replaceWith(faContainer);
DataStore.setBDData("bdfavemotes", btoa(JSON.stringify(this.favoriteEmotes)));
};
export default new QuickEmoteMenu();

View File

@ -5,6 +5,7 @@ import BDEmote from "../ui/emote";
import BdApi from "./pluginapi";
import DataStore from "./datastore";
import {DiscordModules} from "./webpackmodules";
import {Toasts} from "ui";
const bdEmoteSettingIDs = {
TwitchGlobal: "bda-es-7",
@ -192,7 +193,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) {
const exists = _fs.existsSync(file);
if (exists && this.isCacheValid()) {
if (SettingsCookie["fork-ps-2"]) BdApi.showToast("Loading emotes from cache.", {type: "info"});
if (SettingsCookie["fork-ps-2"]) Toasts.show("Loading emotes from cache.", {type: "info"});
Utilities.log("Emotes", "Loading emotes from local cache.");
const data = await new Promise(resolve => {
@ -211,7 +212,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) {
}
if (isValid) {
if (SettingsCookie["fork-ps-2"]) BdApi.showToast("Emotes successfully loaded.", {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show("Emotes successfully loaded.", {type: "success"});
return;
}
@ -220,7 +221,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) {
}
if (!SettingsCookie["fork-es-3"]) return;
if (SettingsCookie["fork-ps-2"]) BdApi.showToast("Downloading emotes in the background do not reload.", {type: "info"});
if (SettingsCookie["fork-ps-2"]) Toasts.show("Downloading emotes in the background do not reload.", {type: "info"});
for (const e in emoteInfo) {
await new Promise(r => setTimeout(r, 1000));
@ -228,7 +229,7 @@ EmoteModule.prototype.loadEmoteData = async function(emoteInfo) {
Emotes[emoteInfo[e].variable] = data;
}
if (SettingsCookie["fork-ps-2"]) BdApi.showToast("All emotes successfully downloaded.", {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show("All emotes successfully downloaded.", {type: "success"});
try { _fs.writeFileSync(file, JSON.stringify(Emotes), "utf8"); }
catch (err) { Utilities.err("Emotes", "Could not save emote data.", err); }

View File

@ -11,7 +11,6 @@ import ContentManager from "./contentmanager";
import DataStore from "./datastore";
// import DevMode from "./devmode";
import Events from "./emitter";
import EmoteMenu from "./emotemenu";
import EmoteModule from "./emotes";
import PluginManager from "./pluginmanager";
// import PublicServers from "./publicservers";
@ -21,7 +20,7 @@ export const React = DiscordModules.React;
export const ReactDOM = DiscordModules.ReactDOM;
export {BDV2, BdApi, Core, ContentManager, DataStore,
Events, EmoteMenu, EmoteModule, PluginManager, /*PublicServers,*/ ThemeManager,
Events, EmoteModule, PluginManager, /*PublicServers,*/ ThemeManager,
Utilities, WebpackModules, DiscordModules};

View File

@ -2,7 +2,7 @@ import {Plugins, SettingsCookie, PluginCookie, ThemeCookie} from "data";
import Utilities from "./utilities";
import WebpackModules, {DiscordModules} from "./webpackmodules";
import DataStore from "./datastore";
import Core from "./core";
import {Toasts, Modals} from "ui";
const BdApi = {
get React() { return DiscordModules.React; },
@ -76,28 +76,13 @@ BdApi.getPlugin = function (name) {
return null;
};
//Get BetterDiscord Core
BdApi.getCore = function () {
return Core;
};
/**
* Shows a generic but very customizable modal.
* @param {string} title - title of the modal
* @param {string} content - a string of text to display in the modal
*/
BdApi.alert = function (title, content) {
const ModalStack = BdApi.findModuleByProps("push", "update", "pop", "popWithKey");
const AlertModal = BdApi.findModuleByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");
if (!ModalStack || !AlertModal) return Core.alert(title, content);
ModalStack.push(function(props) {
return BdApi.React.createElement(AlertModal, Object.assign({
title: title,
body: content,
}, props));
});
Modals.alert(title, content);
};
/**
@ -112,33 +97,12 @@ BdApi.alert = function (title, content) {
* @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
*/
BdApi.showConfirmationModal = function (title, content, options = {}) {
const ModalStack = BdApi.findModuleByProps("push", "update", "pop", "popWithKey");
const TextElement = BdApi.findModuleByProps("Sizes", "Weights");
const ConfirmationModal = BdApi.findModule(m => m.defaultProps && m.key && m.key() == "confirm-modal");
if (!ModalStack || !ConfirmationModal || !TextElement) return Core.alert(title, content);
const {onConfirm, onCancel, confirmText, cancelText, danger = false} = options;
if (typeof(content) == "string") content = TextElement({color: TextElement.Colors.PRIMARY, children: [content]});
else if (Array.isArray(content)) content = TextElement({color: TextElement.Colors.PRIMARY, children: content});
content = [content];
const emptyFunction = () => {};
ModalStack.push(function(props) {
return BdApi.React.createElement(ConfirmationModal, Object.assign({
header: title,
children: content,
red: danger,
confirmText: confirmText ? confirmText : "Okay",
cancelText: cancelText ? cancelText : "Cancel",
onConfirm: onConfirm ? onConfirm : emptyFunction,
onCancel: onCancel ? onCancel : emptyFunction
}, props));
});
return Modals.showConfirmationModal(title, content, options);
};
//Show toast alert
BdApi.showToast = function(content, options = {}) {
Core.showToast(content, options);
Toasts.show(content, options);
};
// Finds module

View File

@ -1,10 +1,9 @@
import {SettingsCookie, PluginCookie, Plugins} from "data";
import ContentManager from "./contentmanager";
import Utilities from "./utilities";
import Core from "./core";
import BdApi from "./pluginapi";
import Emitter from "./emitter";
import DataStore from "./datastore";
import {Toasts, Modals} from "ui";
function PluginModule() {
@ -34,7 +33,7 @@ PluginModule.prototype.loadPlugins = function () {
if (PluginCookie[name]) {
try {
plugin.start();
if (SettingsCookie["fork-ps-2"]) Core.showToast(`${plugin.getName()} v${plugin.getVersion()} has started.`);
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin.getName()} v${plugin.getVersion()} has started.`);
}
catch (err) {
PluginCookie[name] = false;
@ -53,10 +52,10 @@ PluginModule.prototype.loadPlugins = function () {
PluginModule.prototype.startPlugin = function(plugin, reload = false) {
try {
Plugins[plugin].plugin.start();
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} has started.`);
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} has started.`);
}
catch (err) {
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} could not be started.`, {type: "error"});
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} could not be started.`, {type: "error"});
PluginCookie[plugin] = false;
this.savePluginData();
Utilities.err("Plugins", plugin + " could not be started.", err);
@ -66,10 +65,10 @@ PluginModule.prototype.startPlugin = function(plugin, reload = false) {
PluginModule.prototype.stopPlugin = function(plugin, reload = false) {
try {
Plugins[plugin].plugin.stop();
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} has stopped.`);
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} has stopped.`);
}
catch (err) {
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} could not be stopped.`, {type: "error"});
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Plugins[plugin].plugin.getName()} v${Plugins[plugin].plugin.getVersion()} could not be stopped.`, {type: "error"});
Utilities.err("Plugins", Plugins[plugin].plugin.getName() + " could not be stopped.", err);
}
};
@ -96,15 +95,15 @@ PluginModule.prototype.togglePlugin = function (plugin) {
PluginModule.prototype.loadPlugin = function(filename) {
const error = ContentManager.loadContent(filename, "plugin");
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${filename} could not be loaded.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${filename} could not be loaded.`, {type: "error"});
return Utilities.err("ContentManager", `${filename} could not be loaded.`, error);
}
const plugin = Object.values(Plugins).find(p => p.filename == filename).plugin;
try { if (plugin.load && typeof(plugin.load) == "function") plugin.load();}
catch (err) {if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({plugins: [err]});}
catch (err) {if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({plugins: [err]});}
Utilities.log("ContentManager", `${plugin.getName()} v${plugin.getVersion()} was loaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${plugin.getName()} v${plugin.getVersion()} was loaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin.getName()} v${plugin.getVersion()} was loaded.`, {type: "success"});
Emitter.dispatch("plugin-loaded", plugin.getName());
};
@ -116,12 +115,12 @@ PluginModule.prototype.unloadPlugin = function(filenameOrName) {
const error = ContentManager.unloadContent(Plugins[plugin].filename, "plugin");
delete Plugins[plugin];
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${plugin} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
return Utilities.err("ContentManager", `${plugin} could not be unloaded. It may have not been loaded yet.`, error);
}
Utilities.log("ContentManager", `${plugin} was unloaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${plugin} was unloaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin} was unloaded.`, {type: "success"});
Emitter.dispatch("plugin-unloaded", plugin);
};
@ -133,14 +132,14 @@ PluginModule.prototype.reloadPlugin = function(filenameOrName) {
if (enabled) this.stopPlugin(plugin, true);
const error = ContentManager.reloadContent(Plugins[plugin].filename, "plugin");
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${plugin} could not be reloaded.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({plugins: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin} could not be reloaded.`, {type: "error"});
return Utilities.err("ContentManager", `${plugin} could not be reloaded.`, error);
}
if (Plugins[plugin].plugin.load && typeof(Plugins[plugin].plugin.load) == "function") Plugins[plugin].plugin.load();
if (enabled) this.startPlugin(plugin, true);
Utilities.log("ContentManager", `${plugin} v${Plugins[plugin].plugin.getVersion()} was reloaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${plugin} v${Plugins[plugin].plugin.getVersion()} was reloaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${plugin} v${Plugins[plugin].plugin.getVersion()} was reloaded.`, {type: "success"});
Emitter.dispatch("plugin-reloaded", plugin);
};

View File

@ -1,13 +1,16 @@
import {SettingsCookie} from "data";
// import ClassNormalizer from "./classnormalizer";
import DataStore from "./datastore";
import ContentManager from "./contentmanager";
import BdApi from "./pluginapi";
import Core from "./core";
import EmoteModule from "./emotes";
// import DevMode from "./devmode";
import Events from "./emitter";
import WebpackModules from "./webpackmodules";
import {SettingsPanel as SettingsRenderer} from "ui";
import Utilities from "./utilities";
//WebpackModules.getModule(m => m.getSection && m.getProps && !m.getGuildId && !m.getChannel)
//WebpackModules.getByProps("getGuildId", "getSection")
export default new class SettingsPanel {
@ -19,6 +22,62 @@ export default new class SettingsPanel {
this.renderer.renderSidebar();
}
initialize() {
DataStore.initialize();
if (!DataStore.getSettingGroup("settings")) return this.saveSettings();
const savedSettings = this.loadSettings();
$("<style id=\"customcss\">").text(atob(DataStore.getBDData("bdcustomcss"))).appendTo(document.head);
for (const setting in savedSettings) {
if (savedSettings[setting] !== undefined) SettingsCookie[setting] = savedSettings[setting];
}
this.saveSettings();
// console.log("PATCHING");
// Utilities.monkeyPatch(WebpackModules.getByProps("getUserSettingsSections").default.prototype, "render", {after: (data) => {
// data.returnValue.type;
// }});
// Patcher.after(temp2.prototype, "generateSections", (t,a,r) => {
// r.push({section: "DIVIDER"});
// r.push({section: "HEADER", label: "My Section"});
// r.push({color: "#ffffff", label: "My Tab", onClick: function() {console.log("CLICK");}, section: "My Section"});
// r.push({color: "#cccccc", label: "My Tab2", onClick: function() {console.log("CLICK2");}, section: "My Section"});
// })
this.patchSections();
}
async patchSections() {
const UserSettings = await this.getUserSettings(); // data.returnValue.type;
Utilities.monkeyPatch(UserSettings.prototype, "generateSections", {after: (data) => {
console.log(data);
data.returnValue.splice(23, 0, {section: "DIVIDER"});
data.returnValue.splice(24, 0, {section: "HEADER", label: "BandagedBD"});
data.returnValue.splice(25, 0, {section: "IJ1", label: "Injected Tab 1", element: () => this.renderer.core2});
data.returnValue.splice(26, 0, {section: "IJ2", label: "Injected Tab 2", onClick: function() {console.log("CLICK2");}});
}});
const viewClass = WebpackModules.getByProps("standardSidebarView").standardSidebarView.split(" ")[0];
const node = document.querySelector(`.${viewClass}`);
Utilities.getInternalInstance(node).return.return.return.return.return.return.stateNode.forceUpdate();
}
getUserSettings() {
return new Promise(resolve => {
const cancel = Utilities.monkeyPatch(WebpackModules.getByProps("getUserSettingsSections").default.prototype, "render", {after: (data) => {
resolve(data.returnValue.type);
data.thisObject.forceUpdate();
cancel();
}});
});
}
saveSettings() {
DataStore.setSettingGroup("settings", SettingsCookie);
}
loadSettings() {
return DataStore.getSettingGroup("settings");
}
updateSettings(id, enabled) {
Events.dispatch("setting-updated", "Modules", id, enabled);
SettingsCookie[id] = enabled;
@ -46,7 +105,7 @@ export default new class SettingsPanel {
else BdApi.setWindowPreference("backgroundColor", "#2f3136");
}
Core.saveSettings();
this.saveSettings();
}
initializeSettings() {
@ -57,6 +116,6 @@ export default new class SettingsPanel {
ContentManager.watchContent("theme");
}
Core.saveSettings();
this.saveSettings();
}
};

View File

@ -1,10 +1,9 @@
import {SettingsCookie, ThemeCookie, Themes} from "data";
import ContentManager from "./contentmanager";
import Utilities from "./utilities";
import Core from "./core";
import BdApi from "./pluginapi";
import Emitter from "./emitter";
import DataStore from "./datastore";
import {Toasts, Modals} from "ui";
function ThemeModule() {
@ -32,14 +31,14 @@ ThemeModule.prototype.enableTheme = function(theme, reload = false) {
ThemeCookie[theme] = true;
this.saveThemeData();
$("head").append($("<style>", {id: Utilities.escapeID(theme), text: unescape(Themes[theme].css)}));
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Themes[theme].name} v${Themes[theme].version} has been applied.`);
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Themes[theme].name} v${Themes[theme].version} has been applied.`);
};
ThemeModule.prototype.disableTheme = function(theme, reload = false) {
ThemeCookie[theme] = false;
this.saveThemeData();
$(`#${Utilities.escapeID(Themes[theme].name)}`).remove();
if (SettingsCookie["fork-ps-2"] && !reload) Core.showToast(`${Themes[theme].name} v${Themes[theme].version} has been disabled.`);
if (SettingsCookie["fork-ps-2"] && !reload) Toasts.show(`${Themes[theme].name} v${Themes[theme].version} has been disabled.`);
};
ThemeModule.prototype.toggleTheme = function(theme) {
@ -50,13 +49,13 @@ ThemeModule.prototype.toggleTheme = function(theme) {
ThemeModule.prototype.loadTheme = function(filename) {
const error = ContentManager.loadContent(filename, "theme");
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
return Utilities.err("ContentManager", `${filename} could not be loaded.`, error);
}
const theme = Object.values(Themes).find(p => p.filename == filename);
Utilities.log("ContentManager", `${theme.name} v${theme.version} was loaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
Emitter.dispatch("theme-loaded", theme.name);
};
@ -68,12 +67,12 @@ ThemeModule.prototype.unloadTheme = function(filenameOrName) {
const error = ContentManager.unloadContent(Themes[theme].filename, "theme");
delete Themes[theme];
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
return Utilities.err("ContentManager", `${theme} could not be unloaded. It may have not been loaded yet.`, error);
}
Utilities.log("ContentManager", `${theme} was unloaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} was unloaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${theme} was unloaded.`, {type: "success"});
Emitter.dispatch("theme-unloaded", theme);
};
@ -84,12 +83,12 @@ ThemeModule.prototype.reloadTheme = function(filenameOrName) {
const error = ContentManager.reloadContent(Themes[theme].filename, "theme");
if (ThemeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
if (error) {
if (SettingsCookie["fork-ps-1"]) Core.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} could not be reloaded.`, {type: "error"});
if (SettingsCookie["fork-ps-1"]) Modals.showContentErrors({themes: [error]});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${theme} could not be reloaded.`, {type: "error"});
return Utilities.err("ContentManager", `${theme} could not be reloaded.`, error);
}
Utilities.log("ContentManager", `${theme} v${Themes[theme].version} was reloaded.`);
if (SettingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} v${Themes[theme].version} was reloaded.`, {type: "success"});
if (SettingsCookie["fork-ps-2"]) Toasts.show(`${theme} v${Themes[theme].version} was reloaded.`, {type: "success"});
Emitter.dispatch("theme-reloaded", theme);
};

View File

@ -1,5 +1,6 @@
import {SettingsCookie} from "data";
import {BDV2, EmoteMenu, DiscordModules} from "modules";
import {BDV2, DiscordModules} from "modules";
import EmoteMenu from "../builtins/emotemenu";
export default class BDEmote extends DiscordModules.React.Component {
constructor(props) {

161
src/ui/modals.js Normal file
View File

@ -0,0 +1,161 @@
import {Utilities, WebpackModules, React} from "modules";
export default class Modals {
static get ModalStack() {return WebpackModules.getByProps("push", "update", "pop", "popWithKey");}
static get AlertModal() {return WebpackModules.getByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");}
static get TextElement() {return WebpackModules.getByProps("Sizes", "Weights");}
static get ConfirmationModal() {return WebpackModules.getModule(m => m.defaultProps && m.key && m.key() == "confirm-modal");}
static default(title, content) {
const modal = $(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
<div class="header header-1R_AjF">
<div class="title">${title}</div>
</div>
<div class="bd-modal-body">
<div class="scroller-wrap fade">
<div class="scroller">
${content}
</div>
</div>
</div>
<div class="footer footer-2yfCgX">
<button type="button">Okay</button>
</div>
</div>
</div>
</div>`);
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
}
static alert(title, content) {
if (this.ModalStack && this.AlertModal) return this.default(title, content);
this.ModalStack.push(function(props) {
return React.createElement(this.AlertModal, Object.assign({
title: title,
body: content,
}, props));
});
}
/**
* Shows a generic but very customizable confirmation modal with optional confirm and cancel callbacks.
* @param {string} title - title of the modal
* @param {(string|ReactElement|Array<string|ReactElement>)} children - a single or mixed array of react elements and strings. Everything is wrapped in Discord's `TextElement` component so strings will show and render properly.
* @param {object} [options] - options to modify the modal
* @param {boolean} [options.danger=false] - whether the main button should be red or not
* @param {string} [options.confirmText=Okay] - text for the confirmation/submit button
* @param {string} [options.cancelText=Cancel] - text for the cancel button
* @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button
* @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button
*/
static showConfirmationModal(title, content, options = {}) {
const TextElement = this.TextElement;
const ConfirmationModal = this.ConfirmationModal;
const ModalStack = this.ModalStack;
if (!this.ModalStack || !this.ConfirmationModal || !this.TextElement) return this.alert(title, content);
const {onConfirm, onCancel, confirmText, cancelText, danger = false} = options;
if (typeof(content) == "string") content = TextElement({color: TextElement.Colors.PRIMARY, children: [content]});
else if (Array.isArray(content)) content = TextElement({color: TextElement.Colors.PRIMARY, children: content});
content = [content];
const emptyFunction = () => {};
ModalStack.push(function(props) {
return React.createElement(ConfirmationModal, Object.assign({
header: title,
children: content,
red: danger,
confirmText: confirmText ? confirmText : "Okay",
cancelText: cancelText ? cancelText : "Cancel",
onConfirm: onConfirm ? onConfirm : emptyFunction,
onCancel: onCancel ? onCancel : emptyFunction
}, props));
});
}
static showContentErrors({plugins: pluginErrors = [], themes: themeErrors = []}) {
if (!pluginErrors || !themeErrors) return;
if (!pluginErrors.length && !themeErrors.length) return;
const modal = $(`<div class="bd-modal-wrapper theme-dark">
<div class="bd-backdrop backdrop-1wrmKB"></div>
<div class="bd-modal bd-content-modal modal-1UGdnR">
<div class="bd-modal-inner inner-1JeGVc">
<div class="header header-1R_AjF"><div class="title">Content Errors</div></div>
<div class="bd-modal-body">
<div class="tab-bar-container">
<div class="tab-bar TOP">
<div class="tab-bar-item">Plugins</div>
<div class="tab-bar-item">Themes</div>
</div>
</div>
<div class="table-header">
<div class="table-column column-name">Name</div>
<div class="table-column column-message">Message</div>
<div class="table-column column-error">Error</div>
</div>
<div class="scroller-wrap fade">
<div class="scroller">
</div>
</div>
</div>
<div class="footer footer-2yfCgX">
<button type="button">Okay</button>
</div>
</div>
</div>
</div>`);
const generateTab = function(errors) {
const container = $(`<div class="errors">`);
for (const err of errors) {
const error = $(`<div class="error">
<div class="table-column column-name">${err.name ? err.name : err.file}</div>
<div class="table-column column-message">${err.message}</div>
<div class="table-column column-error"><a class="error-link" href="">${err.error ? err.error.message : ""}</a></div>
</div>`);
container.append(error);
if (err.error) {
error.find("a").on("click", (e) => {
e.preventDefault();
Utilities.err("ContentManager", `Error details for ${err.name ? err.name : err.file}.`, err.error);
});
}
}
return container;
};
const tabs = [generateTab(pluginErrors), generateTab(themeErrors)];
modal.find(".tab-bar-item").on("click", (e) => {
e.preventDefault();
modal.find(".tab-bar-item").removeClass("selected");
$(e.target).addClass("selected");
modal.find(".scroller").empty().append(tabs[$(e.target).index()]);
});
modal.find(".footer button").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.find(".bd-backdrop").on("click", () => {
modal.addClass("closing");
setTimeout(() => { modal.remove(); }, 300);
});
modal.appendTo("#app-mount");
if (pluginErrors.length) modal.find(".tab-bar-item")[0].click();
else modal.find(".tab-bar-item")[1].click();
}
}

View File

@ -1,5 +1,5 @@
import {SettingsInfo, SettingsCookie, Plugins, Themes} from "data";
import {React, ReactDOM, Utilities, ContentManager, Events, EmoteModule, EmoteMenu, PluginManager, ThemeManager} from "modules";
import {React, ReactDOM, Utilities, ContentManager, Events, EmoteModule, PluginManager, ThemeManager} from "modules";
import Sidebar from "./sidebar";
import Scroller from "../scroller";
import List from "../list";
@ -12,6 +12,7 @@ import ThemeCard from "./themecard";
import ReloadIcon from "../icons/reload";
import CssEditor from "../customcss/editor";
import SettingsGroup from "../settings/settingsgroup";
export default class V2_SettingsPanel {
@ -98,6 +99,12 @@ export default class V2_SettingsPanel {
self.sidebar.render();
}
get core2() {
return this.coreSettings.map(section => {
return React.createElement(SettingsGroup, Object.assign({}, section, {onChange: this.onChange}));
});
}
get coreComponent() {
return React.createElement(Scroller, {contentColumn: true, fade: true, dark: true, children: [
React.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}),
@ -110,7 +117,7 @@ export default class V2_SettingsPanel {
contentColumn: true, fade: true, dark: true, children: [
React.createElement(SettingsPanel, {key: "espanel", title: "Emote Settings", onChange: this.onChange, settings: this.emoteSettings, button: {
title: "Clear Emote Cache",
onClick: () => { EmoteModule.clearEmoteData(); EmoteModule.init(); EmoteMenu.init(); }
onClick: () => { EmoteModule.clearEmoteData(); EmoteModule.init(); }
}}),
React.createElement(Tools, {key: "tools"})
]});

View File

@ -1,5 +1,14 @@
import {React} from "modules";
const flexContainer = "flex-1xMQg5 flex-1O1GKY da-flex da-flex vertical-V37hAW flex-1O1GKY directionColumn-35P_nr justifyStart-2NDFzi alignStretch-DpGPf3 noWrap-3jynv6 switchItem-2hKKKK";
const flexWrap = "flex-1xMQg5 flex-1O1GKY da-flex da-flex horizontal-1ae9ci horizontal-2EEEnY flex-1O1GKY directionRow-3v3tfG justifyStart-2NDFzi alignStart-H-X2h- noWrap-3jynv6";
const flexChild = "flexChild-faoVW3 da-flexChild";
const title = "titleDefault-a8-ZSr title-31JmR4 da-titleDefault da-title";
const switchWrapper = "flexChild-faoVW3 da-flexChild switchEnabled-V2WDBB switch-3wwwcV da-switchEnabled da-switch valueUnchecked-2lU_20 value-2hFrkk sizeDefault-2YlOZr size-3rFEHg themeDefault-24hCdX";
const switchWrapperEnabled = "flexChild-faoVW3 da-flexChild switchEnabled-V2WDBB switch-3wwwcV da-switchEnabled da-switch valueChecked-m-4IJZ value-2hFrkk sizeDefault-2YlOZr size-3rFEHg themeDefault-24hCdX";
const switchClass = "checkboxEnabled-CtinEn checkbox-2tyjJg da-checkboxEnabled da-checkbox";
const description = "description-3_Ncsb formText-3fs7AJ da-description da-formText note-1V3kyJ da-note modeDefault-3a2Ph1 da-modeDefault primary-jw0I4K";
export default class V2C_Switch extends React.Component {
constructor(props) {
@ -19,25 +28,25 @@ export default class V2C_Switch extends React.Component {
const {checked} = this.state;
return React.createElement(
"div",
{className: "ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item"},
{className: `ui-flex flex-vertical flex-justify-start flex-align-stretch flex-nowrap ui-switch-item ${flexContainer}`},
React.createElement(
"div",
{className: "ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap"},
{className: `ui-flex flex-horizontal flex-justify-start flex-align-stretch flex-nowrap ${flexWrap}`},
React.createElement(
"h3",
{className: "ui-form-title h3 margin-reset margin-reset ui-flex-child"},
{className: `ui-form-title h3 margin-reset margin-reset ui-flex-child ${title} ${flexChild}`},
text
),
React.createElement(
"label",
{className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
React.createElement("input", {className: "ui-switch-checkbox", type: "checkbox", checked: checked, onChange: e => this.onChange(e)}),
"div",
{className: `ui-switch-wrapper ui-flex-child ${checked ? switchWrapperEnabled : switchWrapper}`, style: {flex: "0 0 auto"}},
React.createElement("input", {className: `ui-switch-checkbox ${switchClass}`, type: "checkbox", checked: checked, onChange: e => this.onChange(e)}),
React.createElement("div", {className: `ui-switch ${checked ? "checked" : ""}`})
)
),
React.createElement(
"div",
{className: "ui-form-text style-description margin-top-4", style: {flex: "1 1 auto"}},
{className: `ui-form-text style-description margin-top-4 ${description}`, style: {flex: "1 1 auto"}},
info
)
);

View File

@ -1,6 +1,7 @@
import {SettingsCookie, ThemeCookie, Themes} from "data";
import {React, Core, ThemeManager} from "modules";
import {React, ThemeManager} from "modules";
import ReloadIcon from "../icons/reload";
import Toasts from "../toasts";
export default class V2C_ThemeCard extends React.Component {
@ -34,8 +35,8 @@ export default class V2C_ThemeCard extends React.Component {
reload() {
const theme = this.props.theme.name;
const error = ThemeManager.reloadTheme(theme);
if (error) Core.showToast(`Could not reload ${Themes[theme].name}. Check console for details.`, {type: "error"});
else Core.showToast(`${Themes[theme].name} v${Themes[theme].version} has been reloaded.`, {type: "success"});
if (error) Toasts.show(`Could not reload ${Themes[theme].name}. Check console for details.`, {type: "error"});
else Toasts.show(`${Themes[theme].name} v${Themes[theme].version} has been reloaded.`, {type: "success"});
// this.setState(this.state);
this.props.theme = Themes[theme];
this.onReload(this.props.theme.name);

View File

@ -8,7 +8,7 @@ export default class V2C_SettingsTitle extends React.Component {
render() {
return React.createElement(
"h2",
{className: "ui-form-title h2 margin-reset margin-bottom-20 marginTop60-3PGbtK da-marginTop6"},
{className: "ui-form-title h2 margin-reset margin-bottom-20 marginTop60-3PGbtK h2-2gWE-o title-3sZWYQ size16-14cGz5 height20-mO2eIN weightSemiBold-NJexzi defaultColor-1_ajX0 defaultMarginh2-2LTaUL marginBottom20-32qID7"},
this.props.text
);
}

66
src/ui/toasts.js Normal file
View File

@ -0,0 +1,66 @@
import {WebpackModules} from "modules";
const channelsClass = WebpackModules.getByProps("channels").channels.split(" ")[0];
const membersWrapClass = WebpackModules.getByProps("membersWrap").membersWrap.split(" ")[0];
export default class Toasts {
/** 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: ""}));}
/**
* This shows a toast similar to android towards the bottom of the screen.
*
* @param {string} content The string to show in the toast.
* @param {object} options Options object. Optional parameter.
* @param {string} options.type Changes the type of the toast stylistically and semantically. Choices: "", "info", "success", "danger"/"error", "warning"/"warn". Default: ""
* @param {boolean} options.icon Determines whether the icon should show corresponding to the type. A toast without type will always have no icon. Default: true
* @param {number} options.timeout Adjusts the time (in ms) the toast should be shown for before disappearing automatically. Default: 3000
*/
static show(content, options = {}) {
this.ensureContainer();
const {type = "", icon = true, timeout = 3000} = options;
const toastElem = document.createElement("div");
toastElem.classList.add("bd-toast");
if (type) toastElem.classList.add("toast-" + type);
if (type && icon) toastElem.classList.add("icon");
toastElem.innerText = content;
document.querySelector(".bd-toasts").appendChild(toastElem);
setTimeout(() => {
toastElem.classList.add("closing");
setTimeout(() => {
toastElem.remove();
if (!document.querySelectorAll(".bd-toasts .bd-toast").length) document.querySelector(".bd-toasts").remove();
}, 300);
}, timeout);
}
static ensureContainer() {
if (document.querySelector(".bd-toasts")) return;
const container = document.querySelector(`.${channelsClass} + div`);
const memberlist = container.querySelector(`.${membersWrapClass}`);
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("bd-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);
}
}

View File

@ -1,4 +1,5 @@
import SettingsPanel from "./settings/settings";
import * as PublicServers from "./publicservers/publicservers";
export {default as Toasts} from "./toasts";
export {default as Modals} from "./modals";
export {SettingsPanel, PublicServers};