diff --git a/Plugins/ThemeRepo/ThemeRepo.plugin.js b/Plugins/ThemeRepo/ThemeRepo.plugin.js
index ec530dafc3..ee6546bde3 100644
--- a/Plugins/ThemeRepo/ThemeRepo.plugin.js
+++ b/Plugins/ThemeRepo/ThemeRepo.plugin.js
@@ -2,7 +2,7 @@
* @name ThemeRepo
* @author DevilBro
* @authorId 278543574059057154
- * @version 2.1.7
+ * @version 2.1.9
* @description Allows you to download all Themes from BD's Website within Discord
* @invite Jx3TjNS
* @donate https://www.paypal.me/MircoWittrien
@@ -17,12 +17,12 @@ module.exports = (_ => {
"info": {
"name": "ThemeRepo",
"author": "DevilBro",
- "version": "2.1.7",
+ "version": "2.1.9",
"description": "Allows you to download all Themes from BD's Website within Discord"
},
"changeLog": {
- "fixed": {
- "Old META": "Downloading a Theme that still uses the old META Format will now automatically transform it into the new META Format"
+ "progress": {
+ "New Style and Website Store": "The Repo now directly reflects the Themes hosted on https://betterdiscord.app/ and uses a new Card Style"
}
}
};
@@ -65,23 +65,27 @@ module.exports = (_ => {
return template.content.firstElementChild;
}
} : (([Plugin, BDFDB]) => {
- const isBeta = !(window.BdApi && !Array.isArray(BdApi.settings));
var _this;
- var loading, cachedThemes, grabbedThemes, foundThemes, loadedThemes, generatorThemes, updateInterval;
- var list, header, preview, searchTimeout, updateGeneratorTimeout, forceRerenderGenerator, nativeCSS, nativeCSSvars, forcedSort, forcedOrder, showOnlyOutdated;
- var settings = {}, modalSettings = {}, favorites = [], customList = [];
+
+ var list, header, preview;
+
+ var loading, cachedThemes, grabbedThemes, generatorThemes, updateInterval;
+ var searchTimeout, forcedSort, forcedOrder, showOnlyOutdated;
+ var updateGeneratorTimeout, forceRerenderGenerator, nativeCSS, nativeCSSvars;
+
+ var favorites = [];
const themeStates = {
- UPDATED: 0,
+ INSTALLED: 0,
OUTDATED: 1,
DOWNLOADABLE: 2
};
const buttonData = {
- UPDATED: {
+ INSTALLED: {
colorClass: "GREEN",
backgroundColor: "STATUS_GREEN",
icon: "CHECKMARK",
- text: "updated"
+ text: "USER_SETTINGS_VOICE_INSTALLED_LABEL"
},
OUTDATED: {
colorClass: "RED",
@@ -96,20 +100,17 @@ module.exports = (_ => {
text: "download"
}
};
- const favStates = {
- FAVORIZED: 0,
- NOT_FAVORIZED: 1
- };
- const newStates = {
- NEW: 0,
- NOT_NEW: 1
- };
+ const reverseSorts = [
+ "DOWNLOADS", "LIKES", "FAV", "NEW"
+ ];
const sortKeys = {
NAME: "Name",
AUTHOR: "Author",
VERSION: "Version",
DESCRIPTION: "Description",
STATE: "Update State",
+ DOWNLOADS: "Downloads",
+ LIKES: "Likes",
FAV: "Favorites",
NEW: "New Themes"
};
@@ -134,35 +135,30 @@ module.exports = (_ => {
this.closePreview();
}
filterThemes() {
- let themes = Object.keys(loadedThemes).map(url => {
- let theme = loadedThemes[url];
- let instTheme = BDFDB.BDUtils.getTheme(theme.name);
- if (instTheme && instTheme.author && instTheme.author.toUpperCase() == theme.author.toUpperCase()) theme.state = instTheme.version != theme.version ? themeStates.OUTDATED : themeStates.UPDATED;
- else theme.state = themeStates.DOWNLOADABLE;
+ let themes = grabbedThemes.map(theme => {
+ const installedTheme = theme.version && _this.getInstalledTheme(theme);
+ const state = installedTheme ? (BDFDB.NumberUtils.compareVersions(theme.version, _this.getString(installedTheme.version)) ? themeStates.OUTDATED : themeStates.INSTALLED) : themeStates.DOWNLOADABLE;
return {
- url: theme.url,
- search: (theme.name + " " + theme.version + " " + theme.author + " " + theme.description).toUpperCase(),
- name: theme.name,
- version: theme.version,
- author: theme.author,
+ ...theme,
+ search: [theme.name, theme.version, theme.author, theme.description, theme.tags].flat(10).filter(n => typeof n == "string").join(" ").toUpperCase(),
description: theme.description || "No Description found",
- fav: favorites.includes(url) ? favStates.FAVORIZED : favStates.NOT_FAVORIZED,
- new: theme.state == themeStates.DOWNLOADABLE && !cachedThemes.includes(url) ? newStates.NEW : newStates.NOT_NEW,
- state: theme.state,
- css: theme.css,
- fullCSS: theme.fullCSS
+ fav: favorites.includes(theme.id) && 1,
+ new: state == themeStates.DOWNLOADABLE && !cachedThemes.includes(theme.id) && 1,
+ state: state
};
});
- if (!this.props.updated) themes = themes.filter(theme => theme.state != themeStates.UPDATED);
- if (!this.props.outdated) themes = themes.filter(theme => theme.state != themeStates.OUTDATED);
- if (!this.props.downloadable) themes = themes.filter(theme => theme.state != themeStates.DOWNLOADABLE);
- if (this.props.searchString) {
+ if (!this.props.updated) themes = themes.filter(theme => theme.state != themeStates.INSTALLED);
+ if (!this.props.outdated) themes = themes.filter(theme => theme.state != themeStates.OUTDATED);
+ if (!this.props.downloadable) themes = themes.filter(theme => theme.state != themeStates.DOWNLOADABLE);
+ if (this.props.searchString) {
let searchString = this.props.searchString.toUpperCase();
themes = themes.filter(theme => theme.search.indexOf(searchString) > -1);
}
-
- BDFDB.ArrayUtils.keySort(themes, (!this.props.sortKey || this.props.sortKey == "NEW" && !themes.some(theme => theme.new == newStates.NEW) ? Object.keys(sortKeys)[0] : this.props.sortKey).toLowerCase());
+
+ const sortKey = !this.props.sortKey || this.props.sortKey == "NEW" && !themes.some(theme => theme.new) ? Object.keys(sortKeys)[0] : this.props.sortKey;
+ BDFDB.ArrayUtils.keySort(themes, sortKey.toLowerCase());
if (this.props.orderKey == "DESC") themes.reverse();
+ if (reverseSorts.includes(sortKey)) themes.reverse();
return themes;
}
openPreview() {
@@ -275,16 +271,12 @@ module.exports = (_ => {
if (this.props.currentGenerator) this.runInPreview({
reason: "NewTheme",
checked: true,
- css: (loadedThemes[this.props.currentGenerator] || {}).fullCSS
+ css: (generatorThemes.find(t => t.id == this.props.currentGenerator) || {}).fullCSS
});
if (this.props.useLightMode) this.runInPreview({
reason: "DarkLight",
checked: true
});
- if (this.props.useNormalizer) this.runInPreview({
- reason: "Normalize",
- checked: true
- });
if (this.props.useCustomCSS) this.runInPreview({
reason: "CustomCSS",
checked: true
@@ -353,11 +345,41 @@ module.exports = (_ => {
break;
}
}
+ createThemeFile(name, filename, body) {
+ return new Promise(callback => BDFDB.LibraryRequires.fs.writeFile(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), body, error => {
+ if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_fail", `Theme "${name}"`), {type: "danger"});
+ else {
+ BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_success", `Theme "${name}"`), {type: "success"});
+ if (_this.settings.general.rnmStart) BDFDB.TimeUtils.timeout(_ => {
+ if (BDFDB.BDUtils.isThemeEnabled(name) == false) {
+ BDFDB.BDUtils.enableTheme(name, false);
+ BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", name), _this);
+ }
+ }, 3000);
+ callback();
+ }
+ }));
+ }
+ generateTheme(css) {
+ if (!css || !BDFDB.ObjectUtils.is(this.props.generatorValues)) return "";
+ for (let inputId in this.props.generatorValues) if (this.props.generatorValues[inputId].value && this.props.generatorValues[inputId].value.trim() && this.props.generatorValues[inputId].value != this.props.generatorValues[inputId].oldValue) css = css.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(inputId)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(this.props.generatorValues[inputId].oldValue)}`,"g"),`--${inputId}$1: $2${this.props.generatorValues[inputId].value}`);
+ return css;
+ }
+ createFixerCSS(body) {
+ let oldCSS = body.replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r").split("REPLACE_CLASS_");
+ let newCSS = oldCSS.shift();
+ for (let str of oldCSS) {
+ let reg = /([A-z0-9_]+)(.*)/.exec(str);
+ newCSS += BDFDB.dotCN[reg[1]] + reg[2];
+ }
+ return newCSS.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r");
+ }
render() {
let automaticLoading = BDFDB.BDUtils.getSettings(BDFDB.BDUtils.settingsIds.automaticLoading);
if (!this.props.tab) this.props.tab = "Themes";
- this.props.entries = (!loading.is && !BDFDB.ObjectUtils.isEmpty(loadedThemes) ? this.filterThemes() : []).map(theme => BDFDB.ReactUtils.createElement(RepoCardComponent, {
- theme: theme
+
+ this.props.entries = (!loading.is && grabbedThemes.length ? this.filterThemes() : []).map(theme => BDFDB.ReactUtils.createElement(RepoCardComponent, {
+ data: theme
})).filter(n => n);
BDFDB.TimeUtils.timeout(_ => {
@@ -391,16 +413,10 @@ module.exports = (_ => {
children: `${BDFDB.LanguageUtils.LibraryStringsFormat("loading", "Theme Repo")} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}`
})
]
- }) : BDFDB.ReactUtils.forceStyle(BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCN._repolist,
- style: {
- display: "flex",
- flexDirection: "column",
- margin: "unset",
- width: "unset"
- },
+ }) : BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycards,
children: this.props.entries
- }), ["display", "flex-direction", "margin", "width"])
+ })
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, {
tab: "Generator",
@@ -413,9 +429,10 @@ module.exports = (_ => {
label: "Choose a Generator Theme",
basis: "60%",
value: this.props.currentGenerator || "-----",
- options: [{value: "-----", label: "-----"}, nativeCSSvars && {value: "nativediscord", label: "Discord"}].concat((generatorThemes).map(url => ({value: url, label: (loadedThemes[url] || {}).name || "-----"})).sort((x, y) => (x.label < y.label ? -1 : x.label > y.label ? 1 : 0))).filter(n => n),
+ options: [{value: "-----", label: "-----"}, nativeCSSvars && {value: "nativediscord", label: "Discord"}].concat(generatorThemes.map(t => ({value: t.id, label: t.name || "-----"})).sort((x, y) => (x.label < y.label ? -1 : x.label > y.label ? 1 : 0))).filter(n => n),
onChange: value => {
- if (loadedThemes[value] || value == "nativediscord") {
+ let generatorTheme = generatorThemes.find(t => t.id == value);
+ if (generatorTheme || value == "nativediscord") {
if (this.props.currentGenerator) forceRerenderGenerator = true;
this.props.currentGenerator = value;
this.props.currentGeneratorIsNative = value == "nativediscord";
@@ -430,7 +447,7 @@ module.exports = (_ => {
if (preview) this.runInPreview({
reason: "NewTheme",
checked: true,
- css: (loadedThemes[value.value] || {}).fullCSS
+ css: (generatorTheme || {}).fullCSS
});
else this.openPreview();
BDFDB.ReactUtils.forceUpdate(this);
@@ -451,10 +468,11 @@ module.exports = (_ => {
children: "Download",
onClick: _ => {
if (this.props.currentGeneratorIsNative) {
- _this.createThemeFile("Discord.theme.css", `/**\n * @name Discord\n * @description Allow you to easily customize Discord's native Look \n * @author DevilBro\n * @version 1.0.0\n * @authorId 278543574059057154\n * @invite Jx3TjNS\n * @donate https://www.paypal.me/MircoWittrien\n * @patreon https://www.patreon.com/MircoWittrien\n */\n\n` + _this.generateTheme(nativeCSSvars, this.props.generatorValues));
+ this.createThemeFile("Discord", "Discord.theme.css", `/**\n * @name Discord\n * @description Allow you to easily customize Discord's native Look \n * @author DevilBro\n * @version 1.0.0\n * @authorId 278543574059057154\n * @invite Jx3TjNS\n * @donate https://www.paypal.me/MircoWittrien\n * @patreon https://www.patreon.com/MircoWittrien\n */\n\n` + this.generateTheme(nativeCSSvars));
}
- else if (loadedThemes[this.props.currentGenerator]) {
- _this.createThemeFile(loadedThemes[this.props.currentGenerator].name + ".theme.css", _this.generateTheme(loadedThemes[this.props.currentGenerator].fullCSS, this.props.generatorValues));
+ else {
+ let generatorTheme = generatorThemes.find(t => t.id == this.props.currentGenerator);
+ if (generatorTheme) this.createThemeFile(generatorTheme.name, generatorTheme.name + ".theme.css", this.generateTheme(generatorTheme.fullCSS));
}
}
}),
@@ -462,7 +480,8 @@ module.exports = (_ => {
className: BDFDB.disCN.marginbottom20
}),
(_ => {
- let vars = this.props.currentGeneratorIsNative ? nativeCSSvars.split(".theme-dark, .theme-light") : loadedThemes[this.props.currentGenerator].fullCSS.split(":root");
+ let generatorTheme = generatorThemes.find(t => t.id == this.props.currentGenerator);
+ let vars = this.props.currentGeneratorIsNative ? nativeCSSvars.split(".theme-dark, .theme-light") : ((generatorTheme || {}).fullCSS || "").split(":root");
if (vars.length < 2) return null;
vars = vars[1].replace(/\t\(/g, " (").replace(/\r|\t| {2,}/g, "").replace(/\/\*\n*((?!\/\*|\*\/).|\n)*\n+((?!\/\*|\*\/).|\n)*\n*\*\//g, "").replace(/\n\/\*.*?\*\//g, "").replace(/\n/g, "");
vars = vars.split("{");
@@ -514,7 +533,7 @@ module.exports = (_ => {
if (preview) this.runInPreview({
reason: "NewTheme",
checked: true,
- css: _this.generateTheme(this.props.currentGeneratorIsNative ? nativeCSSvars : loadedThemes[this.props.currentGenerator].fullCSS, this.props.generatorValues)
+ css: this.generateTheme(this.props.currentGeneratorIsNative ? nativeCSSvars : (generatorTheme || {}).fullCSS)
});
}, 1000);
}
@@ -531,69 +550,79 @@ module.exports = (_ => {
open: this.props.tab == BDFDB.LanguageUtils.LanguageStrings.SETTINGS,
render: false,
children: [
- !automaticLoading && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
- className: BDFDB.disCN.marginbottom20,
- children: BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCNS.settingsrowtitle + BDFDB.disCNS.settingsrowtitledefault + BDFDB.disCN.cursordefault,
- children: "To experience ThemeRepo in the best way. I would recommend you to enable BD intern reload function, that way all downloaded files are loaded into Discord without the need to reload."
- })
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
+ title: "Show following Themes",
+ children: Object.keys(_this.defaults.filters).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ type: "Switch",
+ theme: this,
+ keys: ["filters", key],
+ label: _this.defaults.filters[key].description,
+ value: _this.settings.filters[key],
+ onChange: value => {
+ this.props[key] = _this.settings.filters[key] = value;
+ BDFDB.ReactUtils.forceUpdate(this);
+ }
+ }))
}),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ Object.keys(_this.defaults.general).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
- margin: 20,
- label: "Preview in light mode",
- value: this.props.useLightMode,
+ theme: this,
+ keys: ["general", key],
+ label: _this.defaults.general[key].description,
+ note: key == "rnmStart" && !automaticLoading && "Automatic Loading has to be enabled",
+ disabled: key == "rnmStart" && !automaticLoading,
+ value: _this.settings.general[key],
onChange: value => {
- this.props.useLightMode = value;
- if (preview) this.runInPreview({
- reason: "DarkLight",
- checked: this.props.useLightMode
- });
- }
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
- type: "Switch",
- margin: 20,
- label: "Preview with useNormalizer classes",
- value: this.props.useNormalizer,
- onChange: value => {
- this.props.useNormalizer = value;
- if (preview) this.runInPreview({
- reason: "Normalize",
- checked: this.props.useNormalizer
- });
- }
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
- type: "Switch",
- margin: 20,
- label: "Include Custom CSS in Preview",
- value: this.props.useCustomCSS,
- onChange: value => {
- this.props.useCustomCSS = value;
- let customCSS = document.querySelector("style#customcss");
- if (preview && customCSS && customCSS.innerText.length > 0) this.runInPreview({
- reason: "CustomCSS",
- checked: this.props.useCustomCSS,
- css: customCSS.innerText
- });
- }
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
- type: "Switch",
- margin: 20,
- label: "Include ThemeFixer CSS in Preview",
- value: this.props.useThemeFixer,
- onChange: value => {
- this.props.useThemeFixer = value;
- BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/ThemeFixer.css", (error, response, body) => {
- if (preview) this.runInPreview({
- reason: "ThemeFixer",
- checked: this.props.useThemeFixer,
- css: _this.createFixerCSS(body)
- });
- });
+ _this.settings.general[key] = value;
+ BDFDB.ReactUtils.forceUpdate(this);
}
+ })),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
+ title: "Preview Settings",
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ label: "Use Light Mode",
+ value: this.props.useLightMode,
+ onChange: value => {
+ this.props.useLightMode = value;
+ if (preview) this.runInPreview({
+ reason: "DarkLight",
+ checked: this.props.useLightMode
+ });
+ }
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ label: "Include Custom CSS",
+ value: this.props.useCustomCSS,
+ onChange: value => {
+ this.props.useCustomCSS = value;
+ let customCSS = document.querySelector("style#customcss");
+ if (preview && customCSS && customCSS.innerText.length > 0) this.runInPreview({
+ reason: "CustomCSS",
+ checked: this.props.useCustomCSS,
+ css: customCSS.innerText
+ });
+ }
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ margin: 20,
+ label: "Include ThemeFixer",
+ value: this.props.useThemeFixer,
+ onChange: value => {
+ this.props.useThemeFixer = value;
+ BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/ThemeFixer.css", (error, response, body) => {
+ if (preview) this.runInPreview({
+ reason: "ThemeFixer",
+ checked: this.props.useThemeFixer,
+ css: this.createFixerCSS(body)
+ });
+ });
+ }
+ })
+ ]
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
type: "Button",
@@ -602,24 +631,17 @@ module.exports = (_ => {
children: "Download",
onClick: _ => {
BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/ThemeFixer.css", (error, response, body) => {
- _this.createThemeFile("ThemeFixer.theme.css", `/**\n * @name ThemeFixer\n * @description ThemeFixerCSS for transparent themes\n * @author DevilBro\n * @version 1.0.3\n * @authorId 278543574059057154\n * @invite Jx3TjNS\n * @donate https://www.paypal.me/MircoWittrien\n * @patreon https://www.patreon.com/MircoWittrien\n */\n\n` + _this.createFixerCSS(body));
+ this.createThemeFile("ThemeFixer", "ThemeFixer.theme.css", `/**\n * @name ThemeFixer\n * @description ThemeFixerCSS for transparent themes\n * @author DevilBro\n * @version 1.0.3\n * @authorId 278543574059057154\n * @invite Jx3TjNS\n * @donate https://www.paypal.me/MircoWittrien\n * @patreon https://www.patreon.com/MircoWittrien\n */\n\n` + this.createFixerCSS(body));
});
}
}),
- Object.keys(modalSettings).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ !automaticLoading && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
className: BDFDB.disCN.marginbottom20,
- type: "Switch",
- plugin: _this,
- keys: ["modalSettings", key],
- label: _this.defaults.modalSettings[key].description,
- note: key == "rnmStart" && !automaticLoading && "Automatic Loading has to be enabled",
- disabled: key == "rnmStart" && !automaticLoading,
- value: this.props[key],
- onChange: value => {
- this.props[key] = modalSettings[key] = value;
- BDFDB.ReactUtils.forceUpdate(this);
- }
- }))
+ children: BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS.settingsrowtitle + BDFDB.disCNS.settingsrowtitledefault + BDFDB.disCN.cursordefault,
+ children: "To experience Theme Repo in the best way. I would recommend you to enable BD's intern Automatic-Loading Feature, that way all downloaded Files are loaded into Discord without the need to reload."
+ })
+ })
].flat(10).filter(n => n)
})
];
@@ -628,177 +650,228 @@ module.exports = (_ => {
const RepoCardComponent = class ThemeCard extends BdApi.React.Component {
render() {
- let buttonConfig = buttonData[(Object.entries(themeStates).find(n => n[1] == this.props.theme.state) || [])[0]];
- return buttonConfig && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.AddonCard, {
- icon: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- className: BDFDB.disCN._repoicon,
- nativeClass: true,
- iconSVG: ``
- }),
- data: this.props.theme,
- controls: [
- this.props.theme.new == newStates.NEW && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, {
- style: {
- borderRadius: 3,
- textTransform: "uppercase",
- background: BDFDB.DiscordConstants.Colors.STATUS_YELLOW
- },
- text: BDFDB.LanguageUtils.LanguageStrings.NEW
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, {
- className: BDFDB.disCN._repocontrolsbutton,
- isFavorite: this.props.theme.fav == favStates.FAVORIZED,
- onClick: value => {
- this.props.theme.fav = value ? favStates.FAVORIZED : favStates.NOT_FAVORIZED;
- if (value) favorites.push(this.props.theme.url);
- else BDFDB.ArrayUtils.remove(favorites, this.props.theme.url, true);
- BDFDB.DataUtils.save(favorites, _this, "favorites");
- }
- }),
- !isBeta && BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCN._repocontrolsbutton,
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
- text: "Go to Source",
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- name: BDFDB.LibraryComponents.SvgIcon.Names.GITHUB,
- className: BDFDB.disCN._repoicon,
- onClick: _ => {
- let gitUrl = null;
- if (this.props.theme.url.indexOf("https://raw.githubusercontent.com") == 0) {
- let temp = this.props.theme.url.replace("//raw.githubusercontent", "//github").split("/");
- temp.splice(5, 0, "blob");
- gitUrl = temp.join("/");
- }
- else if (this.props.theme.url.indexOf("https://gist.githubusercontent.com/") == 0) {
- gitUrl = this.props.theme.url.replace("//gist.githubusercontent", "//gist.github").split("/raw/")[0];
- }
- if (gitUrl) BDFDB.DiscordUtils.openLink(gitUrl);
- }
+ const thumbnailUrl = this.props.data.thumbnailUrl && `https://betterdiscord.app${this.props.data.thumbnailUrl}`;
+ return BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycard,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardheader,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardcoverwrapper,
+ children: [
+ thumbnailUrl && BDFDB.ReactUtils.createElement("img", {
+ className: BDFDB.disCN.discoverycardcover,
+ src: thumbnailUrl,
+ onClick: _ => {
+ let img = document.createElement("img");
+ img.addEventListener("load", function() {
+ BDFDB.LibraryModules.ModalUtils.openModal(modalData => {
+ return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalRoot, Object.assign({
+ className: BDFDB.disCN.imagemodal
+ }, modalData, {
+ size: BDFDB.LibraryComponents.ModalComponents.ModalSize.DYNAMIC,
+ "aria-label": BDFDB.LanguageUtils.LanguageStrings.IMAGE,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ImageModal, {
+ animated: false,
+ src: thumbnailUrl,
+ original: thumbnailUrl,
+ width: this.width,
+ height: this.height,
+ className: BDFDB.disCN.imagemodalimage,
+ shouldAnimate: true,
+ renderLinkComponent: props => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, props)
+ })
+ }), true);
+ });
+ });
+ img.src = thumbnailUrl;
+ }
+ }),
+ this.props.data.new && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, {
+ className: BDFDB.disCN.discoverycardcoverbadge,
+ style: {
+ borderRadius: 3,
+ textTransform: "uppercase",
+ background: BDFDB.DiscordConstants.Colors.STATUS_YELLOW
+ },
+ text: BDFDB.LanguageUtils.LanguageStrings.NEW
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardiconwrapper,
+ children: BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardicon,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ nativeClass: true,
+ iconSVG: ``
+ })
+ })
})
- })
+ ]
}),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Switch, {
- value: list && list.props.currentTheme && list.props.currentTheme.url == this.props.theme.url,
- onChange: value => {
- if (!list) return;
-
- if (list.props.currentTheme) for (let ins of BDFDB.ReactUtils.findOwner(BDFDB.ObjectUtils.get(this, `${BDFDB.ReactUtils.instanceKey}.return`), {name: "ThemeCard", all: true}).filter(ins => ins && ins.props && ins.props.theme && ins.props.theme.url == list.props.currentTheme.url)) BDFDB.ReactUtils.forceUpdate(ins);
-
- if (value) list.props.currentTheme = this.props.theme;
- else delete list.props.currentTheme;
- delete list.props.currentGenerator;
- delete list.props.generatorValues;
-
- if (preview) list.runInPreview({
- reason: "NewTheme",
- checked: value,
- css: this.props.theme.css
- });
- else list.openPreview();
-
- BDFDB.ReactUtils.forceUpdate(this);
- }
- })
- ],
- links: isBeta && [{
- label: "Source",
- icon: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- name: BDFDB.LibraryComponents.SvgIcon.Names.GITHUB,
- nativeClass: true,
- width: 18,
- height: 18
- }),
- onClick: _ => {
- let gitUrl = null;
- if (this.props.theme.url.indexOf("https://raw.githubusercontent.com") == 0) {
- let temp = this.props.theme.url.replace("//raw.githubusercontent", "//github").split("/");
- temp.splice(5, 0, "blob");
- gitUrl = temp.join("/");
- }
- else if (this.props.theme.url.indexOf("https://gist.githubusercontent.com/") == 0) {
- gitUrl = this.props.theme.url.replace("//gist.githubusercontent", "//gist.github").split("/raw/")[0];
- }
- if (gitUrl) BDFDB.DiscordUtils.openLink(gitUrl);
- }
- }],
- buttons: isBeta ? [
- this.props.theme.state != themeStates.DOWNLOADABLE && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
- text: BDFDB.LanguageUtils.LanguageStrings.DELETE,
- children: BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCNS._repobutton + BDFDB.disCNS._repocontrolsbutton + BDFDB.disCN._repobuttondanger,
- onClick: _ => {
- _this.removeTheme(this.props.theme);
- _this.deleteThemeFile(this.props.theme);
- this.props.theme.state = themeStates.DOWNLOADABLE;
- BDFDB.ReactUtils.forceUpdate(this);
- },
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- name: BDFDB.LibraryComponents.SvgIcon.Names.TRASH,
- nativeClass: true,
- color: "#FFFFFF",
- width: 20,
- height: 20
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardinfo,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardtitle,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardname,
+ children: this.props.data.name
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: BDFDB.LanguageUtils.LanguageStrings.FORM_LABEL_VIDEO_PREVIEW,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.disCN.discoverycardtitlebutton,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ nativeClass: true,
+ width: 16,
+ height: 16,
+ name: BDFDB.LibraryComponents.SvgIcon.Names.EYE
+ })
+ }),
+ onClick: _ => {
+ if (!list) return;
+
+ list.props.currentTheme = this.props.data;
+ delete list.props.currentGenerator;
+ delete list.props.generatorValues;
+
+ if (preview) list.runInPreview({
+ reason: "NewTheme",
+ checked: value,
+ css: this.props.data.css
+ });
+ else list.openPreview();
+
+ BDFDB.ReactUtils.forceUpdate(this);
+ }
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, {
+ className: BDFDB.disCN.discoverycardtitlebutton,
+ isFavorite: this.props.data.fav,
+ onClick: value => {
+ this.props.data.fav = value && 1;
+ if (value) favorites.push(this.props.data.id);
+ else BDFDB.ArrayUtils.remove(favorites, this.props.data.id, true);
+ BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(favorites).join(" "), _this, "favorites");
+ }
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardauthor,
+ children: `by ${this.props.data.author}`
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Scrollers.Thin, {
+ className: BDFDB.disCN.discoverycarddescription,
+ children: this.props.data.description
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardfooter,
+ children: [
+ BDFDB.ArrayUtils.is(this.props.data.tags) && this.props.data.tags.length && BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardtags,
+ children: this.props.data.tags.map(tag => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, {
+ className: BDFDB.disCN.discoverycardtag,
+ style: {background: "var(--background-accent)"},
+ text: tag
+ }))
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardcontrols,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardstats,
+ children: [
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardstat,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.discoverycardstaticon,
+ name: BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD
+ }),
+ this.props.data.downloads
+ ]
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.discoverycardstat,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.discoverycardstaticon,
+ name: BDFDB.LibraryComponents.SvgIcon.Names.HEART
+ }),
+ this.props.data.likes
+ ]
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(RepoCardDownloadButtonComponent, {
+ ...buttonData[(Object.entries(themeStates).find(n => n[1] == this.props.data.state) || [])[0]],
+ installed: this.props.data.state == themeStates.INSTALLED,
+ onDownload: _ => {
+ list && BDFDB.LibraryRequires.request(this.props.data.rawSourceUrl, (error, response, body) => {
+ if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("download_fail", `Theme "${this.props.data.name}"`), {type: "danger"});
+ else list.createThemeFile(this.props.data.name, this.props.data.rawSourceUrl.split("/").pop(), body).then(_ => {
+ this.props.data.state = themeStates.INSTALLED;
+ BDFDB.ReactUtils.forceUpdate(this);
+ });
+ });
+ },
+ onDelete: _ => {
+ BDFDB.LibraryRequires.fs.unlink(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), this.props.data.rawSourceUrl.split("/").pop()), error => {
+ if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_fail", `Theme "${this.props.data.name}"`), {type: "danger"});
+ else {
+ BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_success", `Theme "${this.props.data.name}"`));
+ this.props.data.state = themeStates.DOWNLOADABLE;
+ BDFDB.ReactUtils.forceUpdate(this);
+ }
+ });
+ }
+ })
+ ]
+ })
+ ]
})
- })
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
- text: BDFDB.LanguageUtils.LibraryStrings[buttonConfig.text],
- children: BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCNS._repobutton + BDFDB.disCN._repocontrolsbutton,
- style: {backgroundColor: BDFDB.DiscordConstants.Colors[buttonConfig.backgroundColor] || buttonConfig.backgroundColor},
- onClick: _ => {
- _this.downloadTheme(this.props.theme);
- if (list && list.props.rnmStart) BDFDB.TimeUtils.timeout(_ => {
- if (this.props.theme.state == themeStates.UPDATED) _this.applyTheme(this.props.theme);
- }, 3000);
- this.props.theme.state = themeStates.UPDATED;
- BDFDB.ReactUtils.forceUpdate(this);
- },
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- name: BDFDB.LibraryComponents.SvgIcon.Names[buttonConfig.icon],
- nativeClass: true,
- color: "#FFFFFF",
- width: 20,
- height: 20
- })
- })
- })
- ] : [
- this.props.theme.state != themeStates.DOWNLOADABLE && BDFDB.ReactUtils.createElement("div", {
- className: BDFDB.disCN._repocontrolsbutton,
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
- text: BDFDB.LanguageUtils.LanguageStrings.DELETE,
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
- name: BDFDB.LibraryComponents.SvgIcon.Names.NOVA_TRASH,
- className: BDFDB.disCN._repoicon,
- onClick: (e, instance) => {
- _this.removeTheme(this.props.theme);
- _this.deleteThemeFile(this.props.theme);
- this.props.theme.state = themeStates.DOWNLOADABLE;
- BDFDB.ReactUtils.forceUpdate(this);
- }
- })
- })
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, {
- size: BDFDB.LibraryComponents.Button.Sizes.MIN,
- color: BDFDB.LibraryComponents.Button.Colors[buttonConfig.colorClass],
- style: {backgroundColor: BDFDB.DiscordConstants.Colors[buttonConfig.backgroundColor] || buttonConfig.backgroundColor},
- children: BDFDB.LanguageUtils.LibraryStrings[buttonConfig.text],
- onClick: (e, instance) => {
- _this.downloadTheme(this.props.theme);
- if (list && list.props.rnmStart) BDFDB.TimeUtils.timeout(_ => {
- if (this.props.theme.state == themeStates.UPDATED) _this.applyTheme(this.props.theme);
- }, 3000);
- this.props.theme.state = themeStates.UPDATED;
- BDFDB.ReactUtils.forceUpdate(this);
- }
+ ]
})
]
});
}
};
+ const RepoCardDownloadButtonComponent = class ThemeCardDownloadButton extends BdApi.React.Component {
+ render() {
+ return BDFDB.ReactUtils.createElement("button", {
+ className: BDFDB.disCN.discoverycardbutton,
+ style: {backgroundColor: this.props.delete ? BDFDB.DiscordConstants.Colors.STATUS_RED : (BDFDB.DiscordConstants.Colors[this.props.backgroundColor] || this.props.backgroundColor)},
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.discoverycardstaticon,
+ width: 16,
+ height: 16,
+ name: this.props.delete ? BDFDB.LibraryComponents.SvgIcon.Names.TRASH : BDFDB.LibraryComponents.SvgIcon.Names[this.props.icon]
+ }),
+ this.props.delete ? BDFDB.LanguageUtils.LanguageStrings.APPLICATION_CONTEXT_MENU_UNINSTALL : (BDFDB.LanguageUtils.LibraryStringsCheck[this.props.text] ? BDFDB.LanguageUtils.LibraryStrings[this.props.text] : BDFDB.LanguageUtils.LanguageStrings[this.props.text])
+ ],
+ onClick: _ => {
+ if (this.props.delete) typeof this.props.onDelete == "function" && this.props.onDelete();
+ else typeof this.props.onDelete == "function" && this.props.onDownload();
+ },
+ onMouseEnter: this.props.installed && (_ => {
+ this.props.delete = true;
+ BDFDB.ReactUtils.forceUpdate(this);
+ }),
+ onMouseLeave: this.props.installed && (_ => {
+ this.props.delete = false;
+ BDFDB.ReactUtils.forceUpdate(this);
+ })
+ });
+ }
+ };
+
const RepoListHeaderComponent = class ThemeListHeader extends BdApi.React.Component {
componentDidMount() {
header = this;
@@ -818,14 +891,14 @@ module.exports = (_ => {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, {
tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H2,
className: BDFDB.disCN.marginreset,
- children: `Theme Repo — ${loading.is ? 0 : this.props.amount || 0}/${loading.is ? 0 : Object.keys(loadedThemes).length}`
+ children: `Theme Repo — ${loading.is ? 0 : this.props.amount || 0}/${loading.is ? 0 : grabbedThemes.length}`
})
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SearchBar, {
autoFocus: true,
query: this.props.searchString,
- onChange: value => {
+ onChange: (value, instance) => {
if (loading.is) return;
BDFDB.TimeUtils.clear(searchTimeout);
searchTimeout = BDFDB.TimeUtils.timeout(_ => {
@@ -875,11 +948,11 @@ module.exports = (_ => {
label: sortKeys[this.props.sortKey],
value: this.props.sortKey
},
- options: Object.keys(sortKeys).filter(n => n != "NEW" || Object.keys(loadedThemes).some(p => !cachedThemes.includes(p))).map(key => ({
+ options: Object.keys(sortKeys).filter(n => n != "NEW" || grabbedThemes.some(t => !cachedThemes.includes(t.id))).map(key => ({
label: sortKeys[key],
value: key
})),
- onChange: (key, instance) => {
+ onChange: key => {
this.props.sortKey = list.props.sortKey = key;
BDFDB.ReactUtils.forceUpdate(this, list);
}
@@ -896,7 +969,7 @@ module.exports = (_ => {
label: BDFDB.LanguageUtils.LibraryStrings[orderKeys[key]],
value: key
})),
- onChange: (key, instance) => {
+ onChange: key => {
this.props.orderKey = list.props.orderKey = key;
BDFDB.ReactUtils.forceUpdate(this, list);
}
@@ -908,32 +981,30 @@ module.exports = (_ => {
});
}
};
+
return class ThemeRepo extends Plugin {
onLoad () {
_this = this;
loading = {is: false, timeout: null, amount: 0};
-
+
cachedThemes = [];
grabbedThemes = [];
- foundThemes = [];
- loadedThemes = {};
generatorThemes = [];
this.defaults = {
- settings: {
- keepOnTop: {value: false, description: "Keep the Preview Window always on top"},
- notifyOutdated: {value: true, description: "Get a Notification when one of your Themes is outdated"},
- notifyNewentries: {value: true, description: "Get a Notification when there are new Entries in the Repo"}
+ general: {
+ notifyOutdated: {value: true, description: "Get a Notification when one of your Themes is outdated"},
+ notifyNewEntries: {value: true, description: "Get a Notification when there are new Entries in the Repo"},
+ rnmStart: {value: true, description: "Start Theme after Download"}
},
- modalSettings: {
- updated: {value: true, modify: true, description: "Show updated Themes",},
- outdated: {value: true, modify: true, description: "Show outdated Themes"},
- downloadable: {value: true, modify: true, description: "Show downloadable Themes"},
- rnmStart: {value: true, modify: false, description: "Apply Theme after Download"}
+ filters: {
+ updated: {value: true, description: "Updated"},
+ outdated: {value: true, description: "Outdated"},
+ downloadable: {value: true, description: "Downloadable"},
}
};
-
+
this.patchedModules = {
before: {
SettingsView: "render"
@@ -1034,6 +1105,9 @@ module.exports = (_ => {
}
onStart () {
+ // REMOVE 01.05.2021
+ BDFDB.DataUtils.remove(this, "newentriesdata");
+
this.forceUpdateAll();
this.loadThemes();
@@ -1049,83 +1123,7 @@ module.exports = (_ => {
BDFDB.DOMUtils.remove(BDFDB.dotCN._themereponotice, BDFDB.dotCN._themerepoloadingicon);
}
-
- getSettingsPanel (collapseStates = {}) {
- let customUrl = "";
- let settingsPanel, settingsItems = [];
-
- settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
- title: "Settings",
- collapseStates: collapseStates,
- children: Object.keys(settings).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
- type: "Switch",
- plugin: this,
- keys: ["settings", key],
- label: this.defaults.settings[key].description,
- value: settings[key]
- }))
- }));
- settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
- title: "Custom Themes",
- collapseStates: collapseStates,
- children: [
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
- title: "Add Theme:",
- tag: BDFDB.LibraryComponents.FormComponents.FormTitleTags.H3,
- className: BDFDB.disCNS.margintop4 + BDFDB.disCN.marginbottom8,
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
- align: BDFDB.LibraryComponents.Flex.Align.CENTER,
- children: [
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
- children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
- placeholder: "Insert Raw Github Link of Theme (https://raw.githubusercontent.com/...)",
- onChange: value => {customUrl = value.trim();}
- })
- }),
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, {
- onClick: _ => {
- if (customUrl) {
- customList.push(customUrl);
- BDFDB.DataUtils.save(BDFDB.ArrayUtils.removeCopies(customList), this, "custom");
- BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel, collapseStates);
- }
- },
- children: BDFDB.LanguageUtils.LanguageStrings.ADD
- })
- ]
- })
- }),
- customList.length ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
- title: "Custom Theme List:",
- className: BDFDB.disCNS.margintop8 + BDFDB.disCN.marginbottom20,
- children: customList.map(url => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Card, {
- children: url,
- onRemove: _ => {
- BDFDB.ArrayUtils.remove(customList, url, true);
- BDFDB.DataUtils.save(customList, this, "custom");
- BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel, collapseStates);
- }
- }))
- }) : null,
- BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
- type: "Button",
- margin: 0,
- color: BDFDB.LibraryComponents.Button.Colors.RED,
- label: "Remove all custom added Themes",
- onClick: _ => {
- BDFDB.ModalUtils.confirm(this, "Are you sure you want to remove all added Themes from your own List", _ => {
- BDFDB.DataUtils.save([], this, "custom");
- BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel, collapseStates);
- });
- },
- children: BDFDB.LanguageUtils.LanguageStrings.REMOVE
- })
- ].flat(10).filter(n => n)
- }));
-
- return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
- }
-
+
onSettingsClosed () {
if (this.SettingsUpdated) {
delete this.SettingsUpdated;
@@ -1134,12 +1132,8 @@ module.exports = (_ => {
}
forceUpdateAll () {
- settings = BDFDB.DataUtils.get(this, "settings");
- modalSettings = BDFDB.DataUtils.get(this, "modalSettings");
favorites = BDFDB.DataUtils.load(this, "favorites");
- favorites = BDFDB.ArrayUtils.is(favorites) ? favorites : [];
- customList = BDFDB.DataUtils.load(this, "custom");
- customList = BDFDB.ArrayUtils.is(customList) ? customList : [];
+ favorites = (typeof favorites == "string" ? favorites.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n));
BDFDB.PatchUtils.forceAllUpdates(this);
}
@@ -1166,7 +1160,7 @@ module.exports = (_ => {
section: "themerepo",
label: "Theme Repo",
element: _ => {
- let options = Object.assign({}, modalSettings);
+ let options = Object.assign({}, this.settings.filters);
options.updated = options.updated && !showOnlyOutdated;
options.outdated = options.outdated || showOnlyOutdated;
options.downloadable = options.downloadable && !showOnlyOutdated;
@@ -1174,7 +1168,6 @@ module.exports = (_ => {
options.sortKey = forcedSort || Object.keys(sortKeys)[0];
options.orderKey = forcedOrder || Object.keys(orderKeys)[0];
options.useLightMode = BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themelight;
- options.useNormalizer = BDFDB.BDUtils.getSettings(BDFDB.BDUtils.settingsIds.normalizedClasses);
options.useThemeFixer = false;
options.useCustomCSS = false;
@@ -1204,21 +1197,6 @@ module.exports = (_ => {
}
}
- createFixerCSS (body) {
- let oldcss = body.replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r").split("REPLACE_CLASS_");
- let newcss = oldcss.shift();
- for (let str of oldcss) {
- let reg = /([A-z0-9_]+)(.*)/.exec(str);
- newcss += BDFDB.dotCN[reg[1]] + reg[2];
- }
- return newcss.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r");
- }
-
- createGeneratorInputs (theme, generatorValues) {
- if (!BDFDB.ObjectUtils.is(theme) || !BDFDB.ObjectUtils.is(generatorValues) || !theme.fullCSS) return null;
- return inputRefs;
- }
-
generateTheme (fullCSS, generatorValues) {
if (!fullCSS || !BDFDB.ObjectUtils.is(generatorValues)) return "";
for (let inputId in generatorValues) if (generatorValues[inputId].value && generatorValues[inputId].value.trim() && generatorValues[inputId].value != generatorValues[inputId].oldValue) fullCSS = fullCSS.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(inputId)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(generatorValues[inputId].oldValue)}`,"g"),`--${inputId}$1: $2${generatorValues[inputId].value}`);
@@ -1227,57 +1205,29 @@ module.exports = (_ => {
loadThemes () {
BDFDB.DOMUtils.remove(BDFDB.dotCN._themerepoloadingicon);
- let getThemeInfo, outdated = 0, newEntries = 0, i = 0;
- let tags = ["name", "description", "author", "version"];
- let newEntriesData = BDFDB.DataUtils.load(this, "newentriesdata");
- cachedThemes = (newEntriesData.urlbase64 ? atob(newEntriesData.urlbase64).split("\n") : []).concat(customList);
- BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/ThemeList.txt", (error, response, body) => {
- if (!error && body) {
- body = body.replace(/[\r\t]/g, "");
- BDFDB.DataUtils.save(btoa(body), this, "newentriesdata", "urlbase64");
-
- loadedThemes = {};
- grabbedThemes = body.split("\n").filter(n => n);
- foundThemes = grabbedThemes.concat(customList);
-
- loading = {is: true, timeout: BDFDB.TimeUtils.timeout(_ => {
- BDFDB.TimeUtils.clear(loading.timeout);
- if (this.started) {
- if (loading.is && loading.amount < 4) BDFDB.TimeUtils.timeout(_ => {this.loadThemes();}, 10000);
- loading = {is: false, timeout: null, amount: loading.amount};
- }
- }, 1200000), amount: loading.amount+1};
-
- let loadingIcon = BDFDB.DOMUtils.create(themeRepoIcon.replace(/COLOR_1/gi, "var(--bdfdb-blurple)").replace(/COLOR_2/gi, "#72767d"));
- BDFDB.DOMUtils.addClass(loadingIcon, BDFDB.disCN._themerepoloadingicon);
- loadingIcon.addEventListener("mouseenter", _ => {
- BDFDB.TooltipUtils.create(loadingIcon, this.getLoadingTooltipText(), {
- type: "left",
- className: BDFDB.disCN._themerepoloadingtooltip,
- delay: 500,
- style: "max-width: unset;"
- });
- });
- BDFDB.PluginUtils.addLoadingIcon(loadingIcon);
-
- BDFDB.ReactUtils.forceUpdate(list, header);
-
- getThemeInfo(_ => {
- if (!this.started) {
- BDFDB.TimeUtils.clear(loading.timeout);
- return;
- }
+ cachedThemes = BDFDB.DataUtils.load(this, "cached");
+ cachedThemes = (typeof cachedThemes == "string" ? cachedThemes.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n));
+
+ let loadingIcon;
+ let newEntries = 0, outdatedEntries = 0, checkIndex = 0, checksRunning = 0, callbackCalled = false;
+
+ const checkThemeVersion = _ => {
+ if (checksRunning > 20) return;
+ else if (grabbedThemes.every(t => t.loaded || !t.latestSourceUrl) || !this.started || !loading.is) {
+ if (!callbackCalled) {
+ callbackCalled = true;
+ if (!this.started) return BDFDB.TimeUtils.clear(loading.timeout);
BDFDB.TimeUtils.clear(loading.timeout);
BDFDB.DOMUtils.remove(loadingIcon, BDFDB.dotCN._themerepoloadingicon);
loading = {is: false, timeout: null, amount: loading.amount};
BDFDB.LogUtils.log("Finished fetching Themes", this);
- if (list) BDFDB.ReactUtils.forceUpdate(list);
+ BDFDB.ReactUtils.forceUpdate(list);
- if (settings.notifyOutdated && outdated > 0) {
+ if (this.settings.general.notifyOutdated && outdatedEntries > 0) {
let notice = document.querySelector(BDFDB.dotCN._themerepooutdatednotice);
if (notice) notice.close();
- BDFDB.NotificationUtils.notice(this.labels.notice_outdated_themes.replace("{{var0}}", outdated), {
+ BDFDB.NotificationUtils.notice(this.labels.notice_outdated_themes.replace("{{var0}}", outdatedEntries), {
type: "danger",
className: BDFDB.disCNS._themereponotice + BDFDB.disCN._themerepooutdatednotice,
customIcon: themeRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"),
@@ -1292,7 +1242,7 @@ module.exports = (_ => {
});
}
- if (settings.notifyNewEntries && newEntries > 0) {
+ if (this.settings.general.notifyNewEntries && newEntries > 0) {
let notice = document.querySelector(BDFDB.dotCN._themereponewentriesnotice);
if (notice) notice.close();
BDFDB.NotificationUtils.notice(this.labels.notice_new_themes.replace("{{var0}}", newEntries), {
@@ -1311,174 +1261,145 @@ module.exports = (_ => {
});
}
- if (BDFDB.UserUtils.me.id == "278543574059057154") {
- let notice = document.querySelector(BDFDB.dotCN._themerepofailnotice);
- if (notice) notice.close();
- let wrongUrls = [];
- for (let url of foundThemes) if (url && !loadedThemes[url] && !wrongUrls.includes(url)) wrongUrls.push(url);
- if (wrongUrls.length) {
- BDFDB.NotificationUtils.notice(this.labels.notice_failed_themes.replace("{{var0}}", wrongUrls.length), {
- type: "danger",
- className: BDFDB.disCNS._themereponotice + BDFDB.disCN._themerepofailnotice,
- customIcon: themeRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"),
- buttons: [{
- contents: this.labels.list,
- onClick: _ => {
- let toast = BDFDB.NotificationUtils.toast(wrongUrls.join("\n"), {type: "danger"});
- toast.style.setProperty("overflow", "hidden");
- for (let url of wrongUrls) console.log(url);
- }
- }]
- });
+ BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/GeneratorList.txt", (error, response, body) => {
+ if (!error && body) for (let id of "23 40 45 47".replace(/[\r\t]/g, "").split(" ").map(n => parseInt(n)).filter(n => n != null)) {
+ let theme = grabbedThemes.find(t => t.id == id);
+ if (theme) generatorThemes.push(theme);
}
- }
-
- BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/GeneratorList.txt", (error3, response3, body3) => {
- if (!error3 && body3) for (let url of body3.replace(/[\r\t]/g, "").split("\n").filter(n => n)) if (loadedThemes[url]) generatorThemes.push(url);
});
- BDFDB.LibraryRequires.request(document.querySelector("head link[rel='stylesheet'][integrity]").href, (error3, response3, body3) => {
- if (!error3 && body3) {
- nativeCSS = body3;
+ BDFDB.LibraryRequires.request(document.querySelector("head link[rel='stylesheet'][integrity]").href, (error, response, body) => {
+ if (!error && body) {
+ nativeCSS = body;
let theme = BDFDB.DiscordUtils.getTheme();
let vars = (nativeCSS.split(`.${theme}{`)[1] || "").split("}")[0];
nativeCSSvars = vars ? `.theme-dark, .theme-light {${vars}}` : "";
}
else nativeCSS = nativeCSSvars = "";
});
- });
- }
- });
-
- getThemeInfo = (callback) => {
- if (i >= foundThemes.length || !this.started || !loading.is) {
- callback();
+ }
return;
}
- let url = foundThemes[i];
- BDFDB.LibraryRequires.request(url, (error, response, body) => {
- if (!response) {
- if (url && BDFDB.ArrayUtils.getAllIndexes(foundThemes, url).length < 2) foundThemes.push(url);
- }
- else if (body && body.indexOf("404: Not Found") != 0 && response.statusCode == 200) {
- let theme = {}, text = body.trim();
- if ((text.split("*//").length > 1 || text.indexOf("/**") == 0) && text.split("\n").length > 1) {
- let hasMETAline = text.replace(/\s/g, "").indexOf("//META{");
+ else if (checkIndex > grabbedThemes.length) return;
+
+ const theme = grabbedThemes[checkIndex++];
+ if (!theme || !theme.latestSourceUrl) checkThemeVersion();
+ else {
+ checksRunning++;
+ theme.rawSourceUrl = theme.latestSourceUrl.replace("https://github.com/", "https://raw.githubusercontent.com/").replace(/\/blob\/(.{32,})/i, "/$1")
+ BDFDB.LibraryRequires.request(theme.rawSourceUrl, (error, response, body) => {
+ if (body && body.indexOf("404: Not Found") != 0 && response.statusCode == 200) {
+ theme.name = BDFDB.LibraryModules.StringUtils.upperCaseFirstChar((/@name\s+([^\s^\t^\r^\n]+)|\/\/\**META.*["']name["']\s*:\s*["'](.+?)["']/i.exec(body) || []).filter(n => n)[1] || theme.name || "");
+ theme.author = (/@author\s+(.+)|\/\/\**META.*["']author["']\s*:\s*["'](.+?)["']/i.exec(body) || []).filter(n => n)[1] || theme.author;
+ const version = (/@version\s+(.+)|\/\/\**META.*["']version["']\s*:\s*["'](.+?)["']/i.exec(body) || []).filter(n => n)[1];
+ if (version) {
+ theme.version = version;
+ const installedTheme = this.getInstalledTheme(theme);
+ if (installedTheme && BDFDB.NumberUtils.compareVersions(version, this.getString(installedTheme.version))) outdatedEntries++;
+ }
+ let text = body.trim();
+ let hasMETAline = text.replace(/\s/g, "").indexOf("//META{"), newMeta = "";
if (hasMETAline < 20 && hasMETAline > -1) {
- let searchText = text.replace(/\s*:\s*/g, ":").replace(/\s*}\s*/g, "}");
- for (let tag of tags) {
- let result = searchText.split('"' + tag + '":"');
- result = result.length > 1 ? result[1].split('",')[0].split('"}')[0] : null;
- result = result && tag != "version" ? result.charAt(0).toUpperCase() + result.slice(1) : result;
- theme[tag] = result ? result.trim() : result;
- }
- }
- else {
- let searchText = text.replace(/[\r\t| ]*\*\s*/g, "*");
- for (let tag of tags) {
- let result = searchText.split('@' + tag + ' ');
- result = result.length > 1 ? result[1].split('\n')[0] : null;
- result = result && tag != "version" ? result.charAt(0).toUpperCase() + result.slice(1) : result;
- theme[tag] = result ? result.trim() : result;
- }
- }
-
- let valid = true;
- for (let tag of tags) if (theme[tag] === null) {
- if (tag == "author") theme[tag] = "???";
- else valid = false;
- }
- if (valid) {
- let newMeta = "";
- if (hasMETAline < 20 && hasMETAline > -1) {
- let i = 0, j = 0, metaString = "";
- try {
- for (let c of `{${text.split("{").slice(1).join("{")}`) {
- metaString += c;
- if (c == "{") i++;
- else if (c == "}") j++;
- if (i > 0 && i == j) break;
- }
- let metaObj = JSON.parse(metaString);
- newMeta = "/**\n";
- for (let key in metaObj) newMeta += ` * @${key} ${metaObj[key]}\n`;
- newMeta += "*/";
+ let i = 0, j = 0, metaString = "";
+ try {
+ for (let c of `{${text.split("{").slice(1).join("{")}`) {
+ metaString += c;
+ if (c == "{") i++;
+ else if (c == "}") j++;
+ if (i > 0 && i == j) break;
}
- catch (err) {newMeta = "";}
+ let metaObj = JSON.parse(metaString);
+ newMeta = "/**\n";
+ for (let key in metaObj) newMeta += ` * @${key} ${metaObj[key]}\n`;
+ newMeta += "*/";
}
- theme.fullCSS = [newMeta, newMeta ? text.split("\n").slice(1).join("\n") : text].filter(n => n).join("\n");
- theme.css = (hasMETAline < 20 && hasMETAline > -1 ? text.split("\n").slice(1).join("\n") : text).replace(/[\r|\n|\t]/g, "");
- theme.url = url;
- loadedThemes[url] = theme;
- let instTheme = BDFDB.BDUtils.getTheme(theme.name);
- if (instTheme && instTheme.author && instTheme.author.toUpperCase() == theme.author.toUpperCase() && instTheme.version != theme.version) outdated++;
- if (!cachedThemes.includes(url)) newEntries++;
+ catch (err) {newMeta = "";}
}
+ theme.fullCSS = [newMeta, newMeta ? text.split("\n").slice(1).join("\n") : text].filter(n => n).join("\n");
+ theme.css = (hasMETAline < 20 && hasMETAline > -1 ? text.split("\n").slice(1).join("\n") : text).replace(/[\r|\n|\t]/g, "");
}
- }
- i++;
+ if (!cachedThemes.includes(theme.id)) newEntries++;
+
+ theme.loaded = true;
+
+ let loadingTooltip = document.querySelector(BDFDB.dotCN._themerepoloadingtooltip);
+ if (loadingTooltip) loadingTooltip.update(this.getLoadingTooltipText());
+
+ checksRunning--;
+ checkThemeVersion();
+ });
+ }
+ };
+
+ BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/themes", (error, response, body) => {
+ if (!error && body) try {
+ grabbedThemes = BDFDB.ArrayUtils.keySort(JSON.parse(body).filter(n => n), "name");
+ BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(grabbedThemes.map(n => n.id)).join(" "), this, "cached");
- let loadingTooltip = document.querySelector(BDFDB.dotCN._themerepoloadingtooltip);
- if (loadingTooltip) loadingTooltip.update(this.getLoadingTooltipText());
+ loading = {is: true, timeout: BDFDB.TimeUtils.timeout(_ => {
+ BDFDB.TimeUtils.clear(loading.timeout);
+ if (this.started) {
+ if (loading.is && loading.amount < 4) BDFDB.TimeUtils.timeout(_ => this.loadThemes(), 10000);
+ loading = {is: false, timeout: null, amount: loading.amount};
+ }
+ }, 1200000), amount: loading.amount + 1};
- getThemeInfo(callback);
- });
- }
+ loadingIcon = BDFDB.DOMUtils.create(themeRepoIcon.replace(/COLOR_1/gi, "var(--bdfdb-blurple)").replace(/COLOR_2/gi, "#72767d"));
+ BDFDB.DOMUtils.addClass(loadingIcon, BDFDB.disCN._themerepoloadingicon);
+ loadingIcon.addEventListener("mouseenter", _ => {
+ BDFDB.TooltipUtils.create(loadingIcon, this.getLoadingTooltipText(), {
+ type: "left",
+ className: BDFDB.disCN._themerepoloadingtooltip,
+ delay: 500,
+ style: "max-width: unset;"
+ });
+ });
+ BDFDB.PluginUtils.addLoadingIcon(loadingIcon);
+
+ BDFDB.ReactUtils.forceUpdate(list, header);
+
+ for (let i = 0; i <= 20; i++) checkThemeVersion();
+ }
+ catch (err) {BDFDB.NotificationUtils.toast("Failed to load Theme Store", {type: "danger"});}
+ });
}
getLoadingTooltipText () {
- return BDFDB.LanguageUtils.LibraryStringsFormat("loading", `Theme Repo - [${Object.keys(loadedThemes).length}/${Object.keys(grabbedThemes).length}]`);
+ return BDFDB.LanguageUtils.LibraryStringsFormat("loading", `Theme Repo - [${grabbedThemes.filter(n => n.loaded).length}/${grabbedThemes.length}]`);
+ }
+
+ getString (obj) {
+ let string = "";
+ if (typeof obj == "string") string = obj;
+ else if (obj && obj.props) {
+ if (typeof obj.props.children == "string") string = obj.props.children;
+ else if (Array.isArray(obj.props.children)) for (let c of obj.props.children) string += typeof c == "string" ? c : this.getString(c);
+ }
+ return string;
+ }
+
+ getInstalledTheme (theme) {
+ if (!theme || typeof theme.author != "string") return;
+ const iTheme = BDFDB.BDUtils.getTheme(theme.name, false, true);
+ if (iTheme && theme.author.toUpperCase() == this.getString(iTheme.author).toUpperCase()) return iTheme;
+ else if (theme.rawSourceUrl && window.BdApi && BdApi.Themes && typeof BdApi.Themes.getAll == "function") {
+ const filename = theme.rawSourceUrl.split("/").pop();
+ for (let t of BdApi.Themes.getAll()) if (t.filename == filename && theme.author.toUpperCase() == this.getString(t.author).toUpperCase()) return t;
+ }
}
checkForNewThemes () {
- BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/ThemeList.txt", (error, response, result) => {
- if (response && !BDFDB.equals(result.replace(/\t|\r/g, "").split("\n").filter(n => n), grabbedThemes)) {
- loading = {is: false, timeout: null, amount: 0};
- this.loadThemes();
+ BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/themes", (error, response, body) => {
+ if (!error && body) try {
+ if (JSON.parse(body).filter(n => n).length != grabbedThemes.length) {
+ loading = {is: false, timeout: null, amount: 0};
+ this.loadThemes();
+ }
}
+ catch (err) {BDFDB.NotificationUtils.toast("Failed to load Theme Store", {type: "danger"});}
});
}
- downloadTheme (data) {
- BDFDB.LibraryRequires.request(data.url, (error, response, body) => {
- if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("download_fail", `Theme "${data.name}"`), {type: "danger"});
- else this.createThemeFile(data.url.split("/").pop(), body);
- });
- }
-
- createThemeFile (filename, content) {
- BDFDB.LibraryRequires.fs.writeFile(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), content, (error) => {
- if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_fail", `Theme "${filename}"`), {type: "danger"});
- else BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_success", `Theme "${filename}"`), {type: "success"});
- });
- }
-
- applyTheme (data) {
- if (data.name && BDFDB.BDUtils.isThemeEnabled(data.name) == false) {
- let id = data.name.replace(/^[^a-z]+|[^\w-]+/gi, "-");
- BDFDB.DOMUtils.remove(`style#${id}`);
- BDFDB.BDUtils.enableTheme(data.name, false);
- BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", data.name), this);
- }
- }
-
- deleteThemeFile (data) {
- let filename = data.url.split("/").pop();
- BDFDB.LibraryRequires.fs.unlink(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), (error) => {
- if (error) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_fail", `Theme "${filename}"`), {type: "danger"});
- else BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_success", `Theme "${filename}"`));
- });
- }
-
- removeTheme (data) {
- if (data.name && BDFDB.BDUtils.isThemeEnabled(data.name) == true) {
- let id = data.name.replace(/^[^a-z]+|[^\w-]+/gi, "-");
- BDFDB.DOMUtils.remove(`style#${id}`);
- BDFDB.BDUtils.disableTheme(data.name, false);
- BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_stopped", data.name), this);
- }
- }
-
setLabelsByLanguage () {
switch (BDFDB.LanguageUtils.getLanguage().id) {
case "bg": // Bulgarian