Switch how assets are handled

This commit is contained in:
Zack Rauen 2021-04-07 20:31:02 -04:00
parent 1c25997c4a
commit ebba553ed2
20 changed files with 185 additions and 483 deletions

7
assets/emotes/index.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
blocklist: require("./blocklist.json"),
BTTV: require("./bttv.json"),
FrankerFaceZ: require("./frankerfacez.json"),
TwitchGlobal: require("./twitchglobal.json"),
TwitchSubscriber: require("./twitchsubscriber.json")
};

View File

@ -24,6 +24,10 @@
"showToasts": {
"name": "Show Toasts",
"note": "Shows a small notification for important information"
},
"mediaKeys": {
"name": "Disable Media Keys",
"note": "Prevents Discord from hijacking your media keys after playing a video."
}
},
"appearance": {
@ -47,6 +51,10 @@
"hideGiftButton": {
"name": "Hide Gift Button",
"note": "Hides the Nitro Gift button in the textarea"
},
"removeMinimumSize": {
"name": "Remove Minimum Size",
"note": "Removes Discord's forced minimum window size of 940x500"
}
},
"addons": {
@ -109,6 +117,14 @@
"inspectElement": {
"name": "Inspect Element Hotkey",
"note": "Enables the inspect element hotkey (ctrl + shift + c) that is common in most browsers"
},
"devToolsWarning": {
"name": "Stop DevTools Warning",
"note": "Stops Discord from printing out their \"Hold Up!\" message"
},
"debugLogs": {
"name": "Debug Logs",
"note": "Outputs everything from the console into the debug.log file in the BetterDiscord folder"
}
},
"window": {
@ -150,10 +166,6 @@
"animateOnHover": {
"name": "Animate On Hover",
"note": "Only animate the emote modifiers on hover"
},
"mediaKeys": {
"name": "Disable Media Keys",
"note": "Prevents Discord from hijacking your media keys after playing a video."
}
},
"categories": {
@ -231,7 +243,9 @@
"loading": "Loading emotes in the background do not reload.",
"loaded": "All emotes successfully loaded.",
"clearEmotes": "Clear Emote Data",
"favoriteAction": "Favorite!"
"favoriteAction": "Favorite!",
"downloadFailed": "Download Failed",
"failureMessage": "BetterDiscord failed to download emotes, please check your internet connection and firewall."
},
"PublicServers": {
"button": "public",
@ -283,7 +297,6 @@
},
"Startup": {
"notSupported": "Not Supported",
"versionMismatch": "BetterDiscord Injector v{{injector}} is not supported by the latest remote (v{{remote}}).\n\nPlease download the latest version from [GitHub](https://github.com/rauenzi/BetterDiscordApp/releases/latest)",
"incompatibleApp": "BetterDiscord does not work with {{app}}. Please uninstall one of them.",
"updateNow": "Update Now",
"maybeLater": "Maybe Later",

6
assets/locales/index.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
en: require("./en.json"),
de: require("./de.json"),
ja: require("./ja.json"),
sv: require("./sv.json")
};

View File

@ -9,6 +9,7 @@
"build-prod": "npm run build-prod --prefix injector && npm run build-prod --prefix renderer",
"build-injector": "npm run build --prefix injector",
"build-renderer": "npm run build --prefix renderer",
"pack-emotes": "node scripts/emotes.js",
"inject": "node scripts/inject.js",
"lint": "eslint --ext .js common/ && npm run lint --prefix injector && npm run lint --prefix renderer",
"test": "mocha --require @babel/register --recursive \"./tests/renderer/*.js\"",

View File

@ -9,7 +9,7 @@ export default new class StopDevToolsWarning extends Builtin {
enabled() {
// IPC.stopDevtoolsWarning();
DiscordNative?.window?.setDevtoolsCallbacks(null, null);
window?.DiscordNative?.window?.setDevtoolsCallbacks(null, null);
}
disabled() {
@ -17,6 +17,6 @@ export default new class StopDevToolsWarning extends Builtin {
const stringModule = WebpackModules.getByProps("Messages");
const hideModule = WebpackModules.getModule(m => Object.keys(m).some(k => k.startsWith("hide")));
if (!devtoolsModule || !stringModule || !hideModule) return;
devtoolsModule(stringModule, hideModule, DiscordNative);
devtoolsModule(stringModule, hideModule, window?.DiscordNative);
}
};

View File

@ -25,48 +25,57 @@ export default new class EmoteMenu extends Builtin {
enabled() {
this.after(EmojiPicker, "type", (_, __, returnValue) => {
const head = Utilities.getNestedProp(returnValue, "props.children.props.children.props.children.1.props.children.0.props.children.props.children");
const body = Utilities.getNestedProp(returnValue, "props.children.props.children.props.children.1.props.children");
if (!head || !body) return returnValue;
const originalChildren = Utilities.getNestedProp(returnValue, "props.children.props.children");
if (!originalChildren || originalChildren.__patched) return;
returnValue.props.children.props.children = (props) => {
const childrenReturn = Reflect.apply(originalChildren, null, [props]);
const head = Utilities.getNestedProp(childrenReturn, "props.children.props.children.1.props.children.0.props.children.props.children");
const body = Utilities.getNestedProp(childrenReturn, "props.children.props.children.1.props.children");
if (!head || !body) return childrenReturn;
let activePicker = this.getSelected(body);
let isActive = activePicker.id == "bd-emotes";
const tabProps = head[0].props;
if (!isActive && activePicker.id == "emoji" && this.hideEmojis) {
activePicker = {id: "bd-emotes", index: 3};
isActive = true;
}
if (this.hideEmojis) head.splice(head.findIndex(e => e && e.props && e.props.id == "emoji-picker-tab"), 1);
head.push(
React.createElement("div", {
"id": "bd-emotes-tab",
"role": "tab",
"aria-selected": isActive,
"className": tabProps.className,
}, React.createElement(tabProps.children.type, {
viewType: "bd-emotes",
isActive: isActive,
}, "Twitch")
));
if (isActive) {
body[activePicker.index] = React.createElement(EmoteMenuCard, {
type: "twitch",
}, [
React.createElement(Category, {
label: "Favorites",
icon: React.createElement(Favorite, {}),
}, Object.entries(EmoteModule.favorites).map(([emote, url]) => {
return React.createElement(EmoteIcon, {emote, url});
})),
React.createElement(Category, {
label: "Twitch Emotes",
icon: React.createElement(Twitch, {})
}, Object.keys(EmoteModule.getCategory("TwitchGlobal")).map(emote=> {
const url = EmoteModule.getUrl("TwitchGlobal", emote);
return React.createElement(EmoteIcon, {emote, url});
}))
]);
}
let activePicker = this.getSelected(body);
let isActive = activePicker.id == "bd-emotes";
const tabProps = head[0].props;
if (!isActive && activePicker.id == "emoji" && this.hideEmojis) {
activePicker = {id: "bd-emotes", index: 3};
isActive = true;
}
if (this.hideEmojis) head.splice(head.findIndex(e => e && e.props && e.props.id == "emoji-picker-tab"), 1);
head.push(
React.createElement("div", {
"id": "bd-emotes-tab",
"role": "tab",
"aria-selected": isActive,
"className": tabProps.className,
}, React.createElement(tabProps.children.type, {
viewType: "bd-emotes",
isActive: isActive,
}, "Twitch")
));
if (isActive) {
body[activePicker.index] = React.createElement(EmoteMenuCard, {
type: "twitch",
}, [
React.createElement(Category, {
label: "Favorites",
icon: React.createElement(Favorite, {}),
}, Object.entries(EmoteModule.favorites).map(([emote, url]) => {
return React.createElement(EmoteIcon, {emote, url});
})),
React.createElement(Category, {
label: "Twitch Emotes",
icon: React.createElement(Twitch, {})
}, Object.keys(EmoteModule.getCategory("TwitchGlobal")).map(emote=> {
const url = EmoteModule.getUrl("TwitchGlobal", emote);
return React.createElement(EmoteIcon, {emote, url});
}))
]);
}
return childrenReturn;
};
returnValue.props.children.props.children.__patched = true;
});
}

View File

@ -1,11 +1,14 @@
import Builtin from "../../structs/builtin";
import {EmoteConfig} from "data";
import {EmoteConfig, Config} from "data";
import {Utilities, WebpackModules, DataStore, DiscordModules, Events, Settings, Strings} from "modules";
import BDEmote from "../../ui/emote";
import Modals from "../../ui/modals";
import Toasts from "../../ui/toasts";
import FormattableString from "../../structs/string";
const request = require("request");
const path = require("path");
const fs = require("fs");
const EmoteURLs = {
TwitchGlobal: new FormattableString(`https://static-cdn.jtvnw.net/emoticons/v1/{{id}}/1.0`),
@ -25,13 +28,14 @@ const blocklist = [];
const overrides = ["twitch", "subscriber", "bttv", "ffz"];
const modifiers = ["flip", "spin", "pulse", "spin2", "spin3", "1spin", "2spin", "3spin", "tr", "bl", "br", "shake", "shake2", "shake3", "flap"];
export default new class EmoteModule extends Builtin {
export default new class EmoteModule extends Builtin {
get name() {return "Emotes";}
get collection() {return "settings";}
get category() {return "general";}
get id() {return "emotes";}
get categories() {return Object.keys(Emotes).filter(k => this.isCategoryEnabled(k));}
get shouldDownload() {return Settings.get("emotes", this.category, "download");}
get asarPath() {return path.join(DataStore.baseFolder, "emotes.asar");}
isCategoryEnabled(id) {return super.get("emotes", "categories", id.toLowerCase());}
@ -63,7 +67,7 @@ export default new class EmoteModule extends Builtin {
async enabled() {
Settings.registerCollection("emotes", "Emotes", EmoteConfig, {title: Strings.Emotes.clearEmotes, onClick: this.resetEmotes.bind(this)});
await this.getBlocklist();
// await this.getBlocklist();
await this.loadEmoteData();
Events.on("emotes-favorite-added", this.addFavorite);
@ -177,30 +181,6 @@ export default new class EmoteModule extends Builtin {
});
}
async getBlocklist() {
try {
const category = "Blocklist";
const exists = DataStore.emotesExist(category);
const valid = await this.isCacheValid(category);
const useCache = (valid) || (!valid && exists && !this.shouldDownload);
const list = useCache ? DataStore.getEmoteData(category) : await this.downloadEmotes(category);
blocklist.push(...list);
}
catch (err) {
// TODO: Log this
}
}
isCacheValid(category) {
return new Promise(resolve => {
const etag = DataStore.getCacheHash("emotes", category);
if (!etag) return resolve(false);
request.head({url: this.getRemoteFile(category), headers: {"If-None-Match": etag}}, (err, resp) => {
resolve(!err && resp.statusCode == 304);
});
});
}
async loadEmoteData(categories) {
if (!categories) categories = this.categories;
if (!Array.isArray(categories)) categories = [categories];
@ -208,21 +188,25 @@ export default new class EmoteModule extends Builtin {
categories = categories.map(k => all.find(c => c.toLowerCase() == k.toLowerCase()));
Toasts.show(Strings.Emotes.loading, {type: "info"});
this.emotesLoaded = false;
const localOutdated = Config.release.tag_name > DataStore.getBDData("emoteVersion");
for (const category of categories) {
const exists = DataStore.emotesExist(category);
const valid = await this.isCacheValid(category);
const useCache = (valid) || (!valid && exists && !this.shouldDownload);
let data = null;
if (useCache) {
this.log(`Loading ${category} emotes from local cache.`);
const cachedData = DataStore.getEmoteData(category);
const hasData = Object.keys(cachedData).length > 0;
if (hasData) data = cachedData;
if (!fs.existsSync(this.asarPath) || (localOutdated && this.shouldDownload)) await this.downloadEmotes();
try {
for (const category of categories) {
this.log(category);
const EmoteData = __non_webpack_require__(path.join(this.asarPath, category.toLowerCase()));
Object.assign(Emotes[category], EmoteData);
delete __non_webpack_require__.cache[path.join(this.asarPath, category.toLowerCase())];
await new Promise(r => setTimeout(r, 1000));
}
if (!data) data = await this.downloadEmotes(category);
Object.assign(Emotes[category], data);
await new Promise(r => setTimeout(r, 1000));
const EmoteData = __non_webpack_require__(path.join(this.asarPath, "blocklist"));
blocklist.push(...EmoteData);
delete __non_webpack_require__.cache[path.join(this.asarPath, "blocklist")];
}
catch (err) {
this.log("Failed to load emotes.");
}
this.emotesLoaded = true;
@ -241,36 +225,32 @@ export default new class EmoteModule extends Builtin {
}
}
downloadEmotes(category) {
const url = this.getRemoteFile(category);
this.log(`Downloading ${category} from ${url}`);
const options = {url: url, timeout: 10000, json: true};
return new Promise(resolve => {
request.get(options, (error, response, parsedData) => {
if (error || response.statusCode != 200) {
this.stacktrace(`Could not download ${category} emotes.`, error);
return resolve({});
}
async downloadEmotes() {
try {
const asar = Config.release.assets.find(a => a.name === "emotes.asar");
this.log(`Downloading emotes from: ${asar.url}`);
const buff = await new Promise((resolve, reject) =>
request(asar.url, {encoding: null, headers: {"User-Agent": "BetterDiscord Emotes", "Accept": "application/octet-stream"}}, (err, resp, body) => {
if (err || resp.statusCode != 200) return reject(err || `${resp.statusCode} ${resp.statusMessage}`);
return resolve(body);
}));
for (const emote in parsedData) {
if (emote.length < 4 || blocklist.includes(emote) || !parsedData[emote]) {
delete parsedData[emote];
continue;
}
// parsedData[emote] = EmoteURLs[category].format({id: parsedData[emote]});
}
DataStore.saveEmoteData(category, parsedData);
DataStore.setCacheHash("emotes", category, response.headers.etag);
resolve(parsedData);
this.log(`Downloaded ${category}`);
});
});
this.log("Successfully downloaded emotes.asar");
const asarPath = this.asarPath;
const originalFs = require("original-fs");
originalFs.writeFileSync(asarPath, buff);
this.log(`Saved emotes.asar to ${asarPath}`);
DataStore.setBDData("emoteVersion", Config.release.tag_name);
}
catch (err) {
this.stacktrace("Failed to download emotes.", err);
Modals.showConfirmationModal(Strings.Emotes.downloadFailed, Strings.Emotes.failureMessage, {cancelText: null});
}
}
resetEmotes() {
const categories = Object.keys(Emotes);
this.unloadEmoteData(categories);
for (const cat of categories) DataStore.invalidateCache("emotes", cat);
this.unloadEmoteData();
DataStore.setBDData("emoteVersion", "0");
this.loadEmoteData();
}
};

View File

@ -13,7 +13,7 @@ export default new class PublicServers extends Builtin {
const GuildList = WebpackModules.find(m => m.type && m.type.displayName == "NavigableGuilds");
const GuildListOld = WebpackModules.findByDisplayName("Guilds");
if (!GuildList && !GuildListOld) this.warn("Can't find GuildList component");
this.guildPatch = this.after(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "type" : "render", this._appendButton);
this.guildPatch = this.after(GuildList ? GuildList : GuildListOld.prototype, GuildList ? "type" : "render", () => {this._appendButton();});
this._appendButton();
}
@ -22,8 +22,15 @@ export default new class PublicServers extends Builtin {
DOM.query("#bd-pub-li").remove();
}
_appendButton() {
async _appendButton() {
await new Promise(r => setTimeout(r, 1000));
const existing = DOM.query("#bd-pub-li");
if (existing) return;
const guilds = DOM.query(`.${DiscordModules.GuildClasses.wrapper} .${DiscordModules.GuildClasses.listItem}`);
if (!guilds) return;
DOM.after(guilds, this.button);
}

View File

@ -1,5 +1,6 @@
export default {
version: process.env.__VERSION__,
release: {assets: []},
// Get from main process
path: "",

View File

@ -1,5 +1,4 @@
export {default as Config} from "./config";
export {default as EmoteConfig} from "./settings/emoteconfig";
export {default as SettingsConfig} from "./settings/config";
export {default as Strings} from "./strings";
export {default as EmoteConfig} from "./emotesettings";
export {default as SettingsConfig} from "./settings";
export {default as Changelog} from "./changelog";

View File

@ -1,312 +0,0 @@
export default {
Panels: {
plugins: "Plugins",
themes: "Themes",
customcss: "Custom CSS"
},
Collections: {
settings: {
name: "Settings",
general: {
name: "General",
emotes: {
name: "Emote System",
note: "Enables BD's emote system"
},
publicServers: {
name: "Public Servers",
note: "Display public servers button"
},
voiceDisconnect: {
name: "Voice Disconnect",
note: "Disconnect from voice server when closing Discord"
},
showToasts: {
name: "Show Toasts",
note: "Shows a small notification for important information"
},
mediaKeys: {
name: "Disable Media Keys",
note: "Prevents Discord from hijacking your media keys after playing a video."
}
},
appearance: {
name: "Appearance",
minimalMode: {
name: "Minimal Mode",
note: "Hide elements and reduce the size of elements"
},
twentyFourHour: {
name: "24-Hour Timestamps",
note: "Converts 12-hour timestamps to 24-hour format"
},
coloredText: {
name: "Colored Text",
note: "Make text colour the same as role color"
},
hideGIFButton: {
name: "Hide GIF Button",
note: "Hides the GIF picker button in the textarea"
},
hideGiftButton: {
name: "Hide Gift Button",
note: "Hides the Nitro Gift button in the textarea"
},
removeMinimumSize: {
name: "Remove Minimum Size",
note: "Removes Discord's forced minimum window size of 940x500"
}
},
addons: {
name: "Addon Manager",
addonErrors: {
name: "Show Addon Errors",
note: "Shows a modal with plugin/theme errors"
},
autoReload: {
name: "Automatic Loading",
note: "Automatically loads, reloads, and unloads plugins and themes"
},
editAction: {
name: "Edit Action",
note: "Where plugins & themes appear when editing",
options: {
detached: "Detached Window",
system: "System Editor"
}
}
},
customcss: {
name: "Custom CSS",
customcss: {
name: "Custom CSS",
note: "Enables the Custom CSS tab"
},
liveUpdate: {
name: "Live Update",
note: "Updates the css as you type"
},
startDetached: {
name: "Start Detached",
note: "Clicking the Custom CSS tab opens the editor in a separate window",
},
nativeOpen: {
name: "Open in Native Editor",
note: "Clicking the Custom CSS tab opens your custom css in your native editor"
},
openAction: {
name: "Editor Location",
note: "Where Custom CSS should open by default",
options: {
settings: "Settings Menu",
detached: "Detached Window",
system: "System Editor"
}
}
},
developer: {
name: "Developer Settings",
debuggerHotkey: {
name: "Debugger Hotkey",
note: "Allows activating debugger when pressing F8"
},
reactDevTools: {
name: "React Developer Tools",
note: "Injects your local installation of React Developer Tools into Discord"
},
inspectElement: {
name: "Inspect Element Hotkey",
note: "Enables the inspect element hotkey (ctrl + shift + c) that is common in most browsers"
},
devToolsWarning: {
name: "Stop DevTools Warning",
note: "Stops Discord from printing out their \"Hold Up!\" message"
},
debugLogs: {
name: "Debug Logs",
note: "Outputs everything from the console into the debug.log file in the BetterDiscord folder"
}
},
window: {
name: "Window Preferences",
transparency: {
name: "Enable Transparency",
note: "Enables the main window to be see-through (requires restart)"
},
frame: {
name: "Window Frame",
note: "Adds the native os window frame to the main window"
}
}
},
emotes: {
name: "Emotes",
general: {
name: "General",
download: {
name: "Download Emotes",
note: "Download emotes whenever they are out of date"
},
emoteMenu: {
name: "Emote Menu",
note: "Show Twitch/Favourite emotes in emote menu"
},
hideEmojiMenu: {
name: "Hide Emoji Menu",
note: "Hides Discord's emoji menu when using emote menu"
},
autoCaps: {
name: "Emote Autocapitalization",
note: "Autocapitalize emote commands"
},
modifiers: {
name: "Show Emote Modifiers",
note: "Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)"
},
animateOnHover: {
name: "Animate On Hover",
note: "Only animate the emote modifiers on hover"
}
},
categories: {
name: "Categories",
twitchglobal: {
name: "Twitch Globals",
note: "Show Twitch global emotes"
},
twitchsubscriber: {
name: "Twitch Subscribers",
note: "Show Twitch subscriber emotes"
},
frankerfacez: {
name: "FrankerFaceZ",
note: "Show emotes from FFZ"
},
bttv: {
name: "BetterTTV",
note: "Show emotes from BTTV"
}
}
}
},
Addons: {
title: "{{name}} v{{version}} by {{author}}",
byline: "by {{author}}",
openFolder: "Open {{type}} Folder",
reload: "Reload",
addonSettings: "Settings",
website: "Website",
source: "Source",
invite: "Support Server",
donate: "Donate",
patreon: "Patreon",
name: "Name",
author: "Author",
version: "Version",
added: "Date Added",
modified: "Date Modified",
search: "Search {{type}}",
editAddon: "Edit",
deleteAddon: "Delete",
confirmDelete: "Are you sure you want to delete {{name}}?",
confirmationText: "You have unsaved changes to {{name}}. Closing this window will lose all those changes.",
enabled: "{{name}} has been enabled.",
disabled: "{{name}} has been disabled.",
couldNotEnable: "{{name}} could not be enabled.",
couldNotDisable: "{{name}} could not be disabled.",
couldNotStart: "{{name}} could not be started.",
couldNotStop: "{{name}} could not be stopped.",
settingsError: "Could not open settings for {{name}}",
methodError: "{{method}} could not be fired.",
unknownAuthor: "Unknown Author",
noDescription: "Description not provided.",
alreadyExists: "There is already a {{type}} with name {{name}}",
alreadWatching: "Already watching addons.",
metaError: "META could not be parsed.",
missingNameData: "META missing name data.",
metaNotFound: "META was not found.",
compileError: "Could not be compiled.",
wasUnloaded: "{{name}} was unloaded.",
blankSlateHeader: "You don't have any {{type}}s!",
blankSlateMessage: "Grab some from [this website]({{link}}) and add them to your {{type}} folder."
},
CustomCSS: {
confirmationText: "You have unsaved changes to your Custom CSS. Closing this window will lose all those changes.",
update: "Update",
save: "Save",
openNative: "Open in System Editor",
openDetached: "Detach Window",
settings: "Editor Settings",
editorTitle: "Custom CSS Editor"
},
Emotes: {
loading: "Loading emotes in the background do not reload.",
loaded: "All emotes successfully loaded.",
clearEmotes: "Clear Emote Data",
favoriteAction: "Favorite!"
},
PublicServers: {
button: "public",
join: "Join",
joining: "Joining",
joined: "Joined",
loading: "Loading",
loadMore: "Load More",
notConnected: "Not Connected",
connectionRequired: "You must connect your account in order to join servers.",
connectionError: "Connection Error",
connectionErrorMessage: "There was an error connecting to DiscordServers.com, it's possible their website/api is down. Please try again later.",
pagination: "Page {{page}} of {{count}}",
search: "Search",
connect: "Connect",
reconnect: "Reconnect",
categories: "Categories",
keywords: "Keywords",
connection: "Connected as: {{username}}#{{discriminator}}",
results: "Showing {{start}}-{{end}} of {{total}} results in {{category}}",
query: "for {{query}}"
},
Modals: {
confirmAction: "Are You Sure?",
okay: "Okay",
done: "Done",
cancel: "Cancel",
nevermind: "Nevermind",
close: "Close",
name: "Name",
message: "Message",
error: "Error",
addonErrors: "Addon Errors",
restartRequired: "Restart Required",
restartNow: "Restart Now",
restartLater: "Restart Later",
additionalInfo: "Additional Info",
restartPrompt: "In order to take effect, Discord needs to be restarted. Do you want to restart now?"
},
ReactDevTools: {
notFound: "Extension Not Found",
notFoundDetails: "Unable to find the React Developer Tools extension on your PC. Please install the extension on your local Chrome installation."
},
Sorting: {
sortBy: "Sort By",
order: "Order",
ascending: "Ascending",
descending: "Descending"
},
Startup: {
notSupported: "Not Supported",
incompatibleApp: "BetterDiscord does not work with {{app}}. Please uninstall one of them.",
updateNow: "Update Now",
maybeLater: "Maybe Later",
updateAvailable: "Update Available",
updateInfo: "There is an update available for BetterDiscord's Injector ({{version}}).\n\nYou can either update and restart now, or later.",
updateFailed: "Could Not Update",
manualUpdate: "Unable to update automatically, please download the installer and reinstall normally.\n\n[Download Installer](https://github.com/rauenzi/BetterDiscordApp/releases/latest)",
jqueryFailed: "jQuery Failed To Load",
jqueryFailedDetails: "jQuery could not be loaded, and some plugins may not work properly. Proceed at your own risk."
},
WindowPrefs: {
enabledInfo: "This option requires a transparent theme in order to work properly. On Windows this may break your aero snapping and maximizing.\n\nIn order to take effect, Discord needs to be restarted. Do you want to restart now?",
disabledInfo: "In order to take effect, Discord needs to be restarted. Do you want to restart now?"
}
};

View File

@ -37,12 +37,13 @@ export default new class Core {
DataStore.initialize();
Logger.log("Startup", "Initializing LocaleManager");
await LocaleManager.initialize();
LocaleManager.initialize();
Logger.log("Startup", "Performing incompatibility checks");
if (window.ED) return Modals.alert(Strings.Startup.notSupported, Strings.Startup.incompatibleApp.format({app: "EnhancedDiscord"}));
if (window.WebSocket && window.WebSocket.name && window.WebSocket.name.includes("Patched")) return Modals.alert(Strings.Startup.notSupported, Strings.Startup.incompatibleApp.format({app: "Powercord"}));
this.checkForUpdate();
Logger.log("Startup", "Initializing Settings");
Settings.initialize();
@ -85,8 +86,6 @@ export default new class Core {
Modals.showChangelogModal(Changelog);
DataStore.setBDData("version", Config.version);
}
this.checkForUpdate();
}
waitForGuilds() {
@ -118,6 +117,7 @@ export default new class Core {
});
const data = await resp.json();
Object.assign(Config.release, data);
const remoteVersion = data.tag_name.startsWith("v") ? data.tag_name.slice(1) : data.tag_name;
const hasUpdate = remoteVersion > Config.version;
if (!hasUpdate) return;

View File

@ -39,7 +39,7 @@ export default new class DataStore {
if (!newStorageExists) fs.mkdirSync(this.baseFolder);
if (!fs.existsSync(this.dataFolder)) fs.mkdirSync(this.dataFolder);
if (!fs.existsSync(this.localeFolder)) fs.mkdirSync(this.localeFolder);
// if (!fs.existsSync(this.localeFolder)) fs.mkdirSync(this.localeFolder);
if (!fs.existsSync(this.emoteFolder)) fs.mkdirSync(this.emoteFolder);
if (!fs.existsSync(this.cacheFile)) fs.writeFileSync(this.cacheFile, JSON.stringify({}));

View File

@ -1,9 +1,7 @@
import DefaultStrings from "../data/strings";
import * as Locales from "../../../assets/locales";
import DiscordModules from "./discordmodules";
import Utilities from "./utilities";
import Events from "./emitter";
import DataStore from "./datastore";
const request = require("request");
const {Dispatcher, DiscordConstants, UserSettingsStore} = DiscordModules;
@ -13,53 +11,28 @@ export default new class LocaleManager {
constructor() {
this.locale = "";
this.strings = Utilities.extend({}, DefaultStrings);
this.strings = Utilities.extend({}, Locales[this.defaultLocale]);
}
async initialize() {
await this.setLocale(this.discordLocale);
initialize() {
this.setLocale(this.discordLocale);
Dispatcher.subscribe(DiscordConstants.ActionTypes.USER_SETTINGS_UPDATE, ({settings}) => {
const newLocale = settings.locale;
if (newLocale && newLocale != this.locale) this.setLocale(newLocale.split("-")[0]);
});
}
async setLocale(newLocale) {
setLocale(newLocale) {
let newStrings;
if (newLocale != this.defaultLocale) {
newStrings = await this.getLocaleStrings(newLocale);
newStrings = Locales[newLocale];
if (!newStrings) return this.setLocale(this.defaultLocale);
}
else {
newStrings = DefaultStrings;
newStrings = Locales[this.defaultLocale];
}
this.locale = newLocale;
Utilities.extend(this.strings, newStrings);
Events.emit("strings-updated");
}
async getLocaleStrings(locale) {
const hash = DataStore.getCacheHash("locales", locale);
if (!hash) return await this.downloadLocale(locale);
const invalid = await this.downloadLocale(locale, hash);
if (!invalid) return DataStore.getLocale(locale);
return invalid;
}
downloadLocale(locale, hash = "") {
return new Promise(resolve => {
const options = {
url: Utilities.repoUrl(`assets/locales/${locale}.json`),
timeout: 2000,
json: true
};
if (hash) options.headers = {"If-None-Match": hash};
request.get(options, (err, resp, newStrings) => {
if (err || resp.statusCode !== 200) return resolve(null);
DataStore.saveLocale(locale, newStrings);
DataStore.setCacheHash("locales", locale, resp.headers.etag);
resolve(newStrings);
});
});
}
};

View File

@ -20,12 +20,12 @@ const BdApi = {
get settings() {return Settings.collections;},
get emotes() {
return new Proxy(Emotes.Emotes, {
get(category) {
get(obj, category) {
if (category === "blocklist") return Emotes.blocklist;
const group = Emotes.Emotes[category];
if (!group) return undefined;
return new Proxy(group, {
get(emote) {return group[emote];},
get(cat, emote) {return group[emote];},
set() {Logger.warn("BdApi.emotes", "Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}
});
},

View File

@ -14,15 +14,15 @@ module.exports = {
path: path.resolve(__dirname, "..", "dist")
},
externals: {
electron: `require("electron")`,
fs: `require("fs")`,
"electron": `require("electron")`,
"fs": `require("fs")`,
"original-fs": `require("original-fs")`,
path: `require("path")`,
request: `require("request")`,
events: `require("events")`,
rimraf: `require("rimraf")`,
yauzl: `require("yauzl")`,
mkdirp: `require("mkdirp")`
"path": `require("path")`,
"request": `require("request")`,
"events": `require("events")`,
"rimraf": `require("rimraf")`,
"yauzl": `require("yauzl")`,
"mkdirp": `require("mkdirp")`
},
resolve: {
extensions: [".js", ".jsx"],

18
scripts/emotes.js Normal file
View File

@ -0,0 +1,18 @@
const path = require("path");
const asar = require("asar");
const emotes = path.resolve(__dirname, "..", "assets", "emotes");
const dist = path.resolve(__dirname, "..", "dist");
const bundleFile = path.join(dist, "emotes.asar");
const makeBundle = function() {
console.log("");
console.log("Generating bundle");
asar.createPackage(emotes, bundleFile).then(() => {
console.log(` ✅ Successfully created bundle ${bundleFile}`);
}).catch(err => {
console.log(` ❌ Could not build bundle: ${err.message}`);
});
};
makeBundle();

View File

@ -5,7 +5,7 @@ const asar = require("asar");
const doSanityChecks = require("./validate");
const buildPackage = require("./package");
const dist = path.join(__dirname, "..", "dist");
const dist = path.resolve(__dirname, "..", "dist");
const bundleFile = path.join(dist, "betterdiscord.asar");
const cleanOldAsar = function() {
@ -19,7 +19,7 @@ const cleanOldAsar = function() {
const makeBundle = function() {
console.log("");
console.log("Generating bundle");
asar.createPackage(dist, bundleFile).then(() => {
asar.createPackageFromFiles(dist, bundleFile, ["dist/injector.js", "dist/package.json", "dist/preload.js", "dist/renderer.js"]).then(() => {
console.log(` ✅ Successfully created bundle ${bundleFile}`);
}).catch(err => {
console.log(` ❌ Could not build bundle: ${err.message}`);
@ -28,5 +28,5 @@ const makeBundle = function() {
doSanityChecks(dist);
buildPackage(dist);
cleanOldAsar();
// cleanOldAsar();
makeBundle();