From b668463c394981c0e29f12f0bad138656db12474 Mon Sep 17 00:00:00 2001 From: _Lighty_ Date: Thu, 27 Feb 2020 21:04:39 +0100 Subject: [PATCH] STR v2.0.10 --- Plugins/SaveToRedux/CHANGELOG.md | 6 + Plugins/SaveToRedux/SaveToRedux.plugin.js | 348 +++++++++++++--------- 2 files changed, 216 insertions(+), 138 deletions(-) diff --git a/Plugins/SaveToRedux/CHANGELOG.md b/Plugins/SaveToRedux/CHANGELOG.md index d2804b1..c1cbc11 100644 --- a/Plugins/SaveToRedux/CHANGELOG.md +++ b/Plugins/SaveToRedux/CHANGELOG.md @@ -1,4 +1,10 @@ # [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog +### 2.0.10 +- Fixed the append random option when a file conflicted not working properly. +- Most of the changes are under the hood and functionally don't make too much of a difference. +- Saving with Save As... or browsing for a folder no longer freezes Discord. +- In the event the plugin cannot start, it will warn you that it failed to start. + ### 2.0.9 - Added new conflict option. If a file already exists, it can open up the Save As... modal to set a custom name instead. - Added a Randomize button to the Save As... modal. diff --git a/Plugins/SaveToRedux/SaveToRedux.plugin.js b/Plugins/SaveToRedux/SaveToRedux.plugin.js index 2e662a6..0945337 100644 --- a/Plugins/SaveToRedux/SaveToRedux.plugin.js +++ b/Plugins/SaveToRedux/SaveToRedux.plugin.js @@ -41,16 +41,16 @@ var SaveToRedux = (() => { twitter_username: '' } ], - version: '2.0.9', + version: '2.0.10', description: 'Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly.', github: 'https://github.com/1Lighty', github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js' }, changelog: [ { - title: 'QOL', - type: 'fixed', - items: ['Added new conflict option. If a file already exists, it can open up the Save As... modal to set a custom name instead.', 'Added a Randomize button to the Save As... modal.', 'Properly sanitizing filenames now.'] + title: 'heck', + type: 'added', + items: ['Fixed the append random option when a file conflicted not working properly.', "Most of the changes are under the hood and functionally don't make too much of a difference.", 'Saving with Save As... or browsing for a folder no longer freezes Discord.', 'In the event the plugin cannot start, it will warn you that it failed to start.'] } ], defaultConfig: [ @@ -103,28 +103,26 @@ var SaveToRedux = (() => { /* Build */ const buildPlugin = ([Plugin, Api]) => { - const { ContextMenu, EmulatedTooltip, Toasts, Settings, Popouts, Modals, Utilities, WebpackModules, Filters, DiscordModules, ColorConverter, DOMTools, DiscordClasses, DiscordSelectors, ReactTools, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities, DiscordClassModules, Structs } = Api; - const { React, ModalStack, ContextMenuActions, ContextMenuItem, ContextMenuItemsGroup, ReactDOM, ChannelStore, GuildStore, UserStore, DiscordConstants, Dispatcher, GuildMemberStore, GuildActions, SwitchRow, EmojiUtils, RadioGroup } = DiscordModules; + const { Settings, Modals, Utilities, WebpackModules, DiscordModules, DiscordClasses, ReactComponents, DiscordAPI, Logger, Patcher, PluginUpdater, PluginUtilities } = Api; + const { React, ContextMenuActions, ReactDOM, GuildStore, DiscordConstants, Dispatcher, SwitchRow, EmojiUtils, RadioGroup } = DiscordModules; + const TextComponent = Utilities.getNestedProp(DiscordModules, 'TextElement.default'); + const getEmojiURL = Utilities.getNestedProp(WebpackModules.getByProps('getEmojiURL'), 'getEmojiURL'); + const showAlertModal = Utilities.getNestedProp( + WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)), + 'show' + ); - const ContextMenuSubMenuItem = WebpackModules.getByDisplayName('FluxContainer(SubMenuItem)'); - const TextComponent = WebpackModules.getByDisplayName('Text'); - const getEmojiURL = WebpackModules.getByProps('getEmojiURL').getEmojiURL; - const showAlertModal = WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)).show; - - const dialog = require('electron').remote.dialog; - const openSaveDialog = dialog.showSaveDialogSync || dialog.showSaveDialog; - const openOpenDialog = dialog.showOpenDialogSync || dialog.showOpenDialog; - const openItem = require('electron').shell.openItem; - const DelayedCall = WebpackModules.getByProps('DelayedCall').DelayedCall; + const dialog = Utilities.getNestedProp(require('electron'), 'remote.dialog'); + const openItem = Utilities.getNestedProp(require('electron'), 'shell.openItem'); + const DelayedCall = Utilities.getNestedProp(WebpackModules.getByProps('DelayedCall'), 'DelayedCall'); const FsModule = require('fs'); const RequestModule = require('request'); const PathModule = require('path'); const MimeTypesModule = require('mime-types'); const FormItem = WebpackModules.getByDisplayName('FormItem'); - const Messages = WebpackModules.getByProps('Messages').Messages; + const Messages = Utilities.getNestedProp(WebpackModules.getByProps('Messages'), 'Messages'); const TextInput = WebpackModules.getByDisplayName('TextInput'); const AvatarModule = WebpackModules.getByProps('getChannelIconURL'); - const MessageShit = WebpackModules.find(m => m.default && m.getMessage); const TrustStore = WebpackModules.getByProps('isTrustedDomain'); @@ -135,7 +133,14 @@ var SaveToRedux = (() => { isTrustedDomain.domains = [/\/\/steamuserimages-\w\.akamaihd\.net\//, /\/\/steamcdn-\w\.akamaihd\.net\//, /\/\/steamcommunity-\w\.akamaihd\.net\//, '//cdn.discordapp.com/', '//media.discordapp.net/', /\/\/images-ext-\d\.discordapp\.net\//, '//i.ytimg.com/', /\/\/static\d\.e621\.net\//, '//pbs.twimg.com/', '//preview.redd.it/', '//cdn.shopify.com/', '//discordapp.com/', '//i.imgur.com/', '//i.clouds.tf/', '//image.prntscr.com/', '//i.giphy.com/', '//media.tenor.co/']; const MessageStrings = { - UNTRUSTED_LINK: MessageShit.getMessage("If you see this, SaveToRedux has failed to get a safe proxied version of the image URL **!!{url}!!**. If you do not recognize the domain, it's best you don't download from it as it could potentially be an IP logger.\n\nAre you sure you want to download an image from this domain?", 'en-US') + UNTRUSTED_LINK: (() => { + try { + return MessageShit.getMessage("If you see this, SaveToRedux has failed to get a safe proxied version of the image URL **!!{url}!!**. If you do not recognize the domain, it's best you don't download from it as it could potentially be an IP logger.\n\nAre you sure you want to download an image from this domain?", 'en-US'); + } catch (e) { + Logger.stacktrace('Failed to create message', e); + return null; + } + })() }; class FolderEditor extends React.Component { @@ -262,6 +267,14 @@ var SaveToRedux = (() => { return '' === o ? l : n(l, ''); } + const faultyVars = []; + { + const vars = { TextComponent, getEmojiURL, openItem, DelayedCall, FormItem, Messages, TextInput, AvatarModule, TrustStore }; + for (const varName in vars) { + if (!vars[varName]) faultyVars.push(varName); + } + } + return class SaveToRedux extends Plugin { constructor() { super(); @@ -276,8 +289,27 @@ var SaveToRedux = (() => { }; XenoLib.DiscordUtils.bindAll(this, ['handleContextMenu', 'formatFilename']); XenoLib.changeName(__filename, 'SaveToRedux'); + const oOnStart = this.onStart.bind(this); + this.onStart = () => { + try { + oOnStart(); + } catch (e) { + Logger.stacktrace('Failed to start!', e); + PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw); + XenoLib.Notifications.error(`[**${this.name}**] Failed to start! Please update it, press CTRL + R, or ${GuildStore.getGuild(XenoLib.supportServerId) ? 'go to <#639665366380838924>' : '[join my support server](https://discord.gg/NYvWdN5)'} for further assistance.`, { timeout: 0 }); + try { + this.onStop(); + } catch (e) {} + } + }; } onStart() { + this.promises = { state: { cancelled: false } }; + if (faultyVars.length) { + Logger.error(`Following vars are invalid: ${faultyVars.map(e => '\n' + e).reduce((e, b) => e + b, '')}`); + PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw); + return XenoLib.Notifications.error(`[**${this.name}**] Plugin is in a broken state. Please update it, press CTRL + R or ${GuildStore.getGuild(XenoLib.supportServerId) ? 'go to <#639665366380838924>' : '[join my support server](https://discord.gg/NYvWdN5)'} for further assistance.`, { timeout: 0 }); + } /* trigger settings migration */ if (typeof this.settings.folders !== 'undefined') { const settings = Utilities.deepclone(this.defaultSettings); @@ -293,7 +325,6 @@ var SaveToRedux = (() => { this.saveSettings(); this.saveFolders(); } - this.promises = { state: { cancelled: false } }; this.folders = XenoLib.loadData(this.name, 'folders', { data: [] }).data; @@ -567,7 +598,6 @@ var SaveToRedux = (() => { ), { confirmText: 'Save', - onCancel: e => console.log('oof'), onConfirm: () => { const onDoShitOrWhateverFuckThisShitMan = val => { if (!folder || folder === -1) return onOk(val); @@ -619,6 +649,7 @@ var SaveToRedux = (() => { } case 3: { path = `${basePath}/${formattedurl.name}-${this.rand()}${formattedurl.extension ? '.' + formattedurl.extension : ''}`; + break; } case 4: return handleSaveAs(true); @@ -761,58 +792,64 @@ var SaveToRedux = (() => { subItems.push( ...folderSubMenus, XenoLib.createContextMenuItem('Add Folder', () => { - const path = openOpenDialog({ - title: 'Add folder', - properties: ['openDirectory', 'createDirectory'] - }); - if (!path) return BdApi.showToast('Maybe next time.'); - let idx; - if ((idx = this.folders.findIndex(m => m.path === path[0])) !== -1) return BdApi.showToast(`Folder already exists as ${this.folders[idx].name}!`, { type: 'error', timeout: 5000 }); - const folderName = PathModule.basename(path[0]); - let __name = folderName; - let __path = path[0].slice(0); - const saveFolder = () => { - if (!__path || !__path.length) return BdApi.showToast('Invalid path', { type: 'error' }); - this.folders.push({ - path: __path, - name: __name || 'Unnamed' + dialog + .showOpenDialog({ + title: 'Add folder', + properties: ['openDirectory', 'createDirectory'] + }) + .then(({ filePaths: [path] }) => { + if (!path) return BdApi.showToast('Maybe next time.'); + let idx; + if ((idx = this.folders.findIndex(m => m.path === path)) !== -1) return BdApi.showToast(`Folder already exists as ${this.folders[idx].name}!`, { type: 'error', timeout: 5000 }); + const folderName = PathModule.basename(path); + let __name = folderName; + let __path = path.slice(0); + const saveFolder = () => { + if (!__path || !__path.length) return BdApi.showToast('Invalid path', { type: 'error' }); + this.folders.push({ + path: __path, + name: __name || 'Unnamed' + }); + this.saveFolders(); + BdApi.showToast('Added!', { type: 'success' }); + }; + Modals.showModal( + 'Create New Folder', + React.createElement(FolderEditor, { + name: folderName, + path: path, + onNameChange: e => (__name = e), + onPathChange: e => (__path = e) + }), + { + confirmText: 'Create', + onConfirm: saveFolder, + size: XenoLib.joinClassNames(Modals.ModalSizes.MEDIUM, 'ST-modal') + } + ); }); - this.saveFolders(); - BdApi.showToast('Added!', { type: 'success' }); - }; - Modals.showModal( - 'Create New Folder', - React.createElement(FolderEditor, { - name: folderName, - path: path[0], - onNameChange: e => (__name = e), - onPathChange: e => (__path = e) - }), - { - confirmText: 'Create', - onConfirm: saveFolder, - size: XenoLib.joinClassNames(Modals.ModalSizes.MEDIUM, 'ST-modal') - } - ); }), XenoLib.createContextMenuItem('Save As...', () => { - const path = openSaveDialog({ - defaultPath: formattedurl.fileName, - filters: formattedurl.extension - ? [ - { - name: /\.{0,1}(png|jpe?g|webp|gif|svg)$/i.test(formattedurl.extension) ? 'Images' : /\.{0,1}(mp4|webm|mov)$/i.test(formattedurl.extension) ? 'Videos' : /\.{0,1}(mp3|ogg|wav|flac)$/i.test(formattedurl.extension) ? 'Audio' : 'Files', - extensions: [formattedurl.extension] - }, - { - name: 'All Files', - extensions: ['*'] - } - ] - : undefined - }); - if (!path) return BdApi.showToast('Maybe next time.'); - saveFile(path, undefined, false, true); + dialog + .showSaveDialog({ + defaultPath: formattedurl.fileName, + filters: formattedurl.extension + ? [ + { + name: /\.{0,1}(png|jpe?g|webp|gif|svg)$/i.test(formattedurl.extension) ? 'Images' : /\.{0,1}(mp4|webm|mov)$/i.test(formattedurl.extension) ? 'Videos' : /\.{0,1}(mp3|ogg|wav|flac)$/i.test(formattedurl.extension) ? 'Audio' : 'Files', + extensions: [formattedurl.extension] + }, + { + name: 'All Files', + extensions: ['*'] + } + ] + : undefined + }) + .then(({ filePath: path }) => { + if (!path) return BdApi.showToast('Maybe next time.'); + saveFile(path, undefined, false, true); + }); }) ); return createSubMenu(`Save ${type} To`, subItems, () => { @@ -978,9 +1015,6 @@ var SaveToRedux = (() => { get [Symbol.toStringTag]() { return 'Plugin'; } - get css() { - return this._css; - } get name() { return config.info.name; } @@ -1008,7 +1042,22 @@ var SaveToRedux = (() => { /* Finalize */ - return !global.ZeresPluginLibrary || !global.XenoLib + /* this new lib loader is lit */ + let ZeresPluginLibraryOutdated = false; + let XenoLibOutdated = false; + try { + if (global.BdApi && 'function' == typeof BdApi.getPlugin) { + const i = (i, n) => ((i = i.split('.').map(i => parseInt(i))), (n = n.split('.').map(i => parseInt(i))), !!(n[0] > i[0]) || !!(n[0] == i[0] && n[1] > i[1]) || !!(n[0] == i[0] && n[1] == i[1] && n[2] > i[2])), + n = (n, e) => n && n._config && n._config.info && n._config.info.version && i(n._config.info.version, e), + e = BdApi.getPlugin('ZeresPluginLibrary'), + o = BdApi.getPlugin('XenoLib'); + n(e, '1.2.10') && (ZeresPluginLibraryOutdated = !0), n(o, '1.3.10') && (XenoLibOutdated = !0); + } + } catch (i) { + console.error('Error checking if libraries are out of date', i); + } + + return !global.ZeresPluginLibrary || !global.XenoLib || ZeresPluginLibraryOutdated || XenoLibOutdated ? class { getName() { return this.name.replace(/\s+/g, ''); @@ -1024,71 +1073,94 @@ var SaveToRedux = (() => { } stop() {} load() { - const ezlibMissing = !global.XenoLib; - const zlibMissing = !global.ZeresPluginLibrary; - const bothLibsMissing = ezlibMissing && zlibMissing; - const header = `Missing ${(bothLibsMissing && 'Libraries') || 'Library'}`; - const content = `The ${(bothLibsMissing && 'Libraries') || 'Library'} ${(zlibMissing && 'ZeresPluginLibrary') || ''} ${(ezlibMissing && (zlibMissing ? 'and XenoLib' : 'XenoLib')) || ''} required for ${this.name} ${(bothLibsMissing && 'are') || 'is'} missing.`; - 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'); - const onFail = () => BdApi.getCore().alert(header, `${content}
Due to a slight mishap however, you'll have to download the libraries yourself. After opening the links, do CTRL + S to download the library.
${(zlibMissing && '
Click here to download ZeresPluginLibrary') || ''}${(zlibMissing && '
Click here to download XenoLib') || ''}`); - if (!ModalStack || !ConfirmationModal || !TextElement) return onFail(); - ModalStack.push(props => { - return BdApi.React.createElement( - ConfirmationModal, - Object.assign( - { - header, - children: [BdApi.React.createElement(TextElement, { color: TextElement.Colors.PRIMARY, children: [`${content} Please click Download Now to install ${(bothLibsMissing && 'them') || 'it'}.`] })], - red: false, - confirmText: 'Download Now', - cancelText: 'Cancel', - onConfirm: () => { - const request = require('request'); - const fs = require('fs'); - const path = require('path'); - const waitForLibLoad = callback => { - if (!global.BDEvents) return callback(); - const onLoaded = e => { - if (e !== 'ZeresPluginLibrary') return; - BDEvents.off('plugin-loaded', onLoaded); - callback(); - }; - BDEvents.on('plugin-loaded', onLoaded); - }; - const onDone = () => { - if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return; - if (!global.BDEvents || global.XenoLib) pluginModule.reloadPlugin(this.name); - else { - const listener = () => { - pluginModule.reloadPlugin(this.name); - BDEvents.off('xenolib-loaded', listener); - }; - BDEvents.on('xenolib-loaded', listener); - } - }; - const downloadXenoLib = () => { - if (global.XenoLib) return onDone(); - request('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (error, response, body) => { - if (error) return onFail(); - onDone(); - fs.writeFile(path.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), body, () => {}); - }); - }; - if (!global.ZeresPluginLibrary) { - request('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (error, response, body) => { - if (error) return onFail(); - waitForLibLoad(downloadXenoLib); - fs.writeFile(path.join(window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), body, () => {}); - }); - } else downloadXenoLib(); - } - }, - props + const a = !global.XenoLib, + b = !global.ZeresPluginLibrary, + c = (a && b) || ((a || b) && (XenoLibOutdated || ZeresPluginLibraryOutdated)) || XenoLibOutdated || ZeresPluginLibraryOutdated, + d = (() => { + let d = ''; + return a || b ? (d += `Missing${XenoLibOutdated || ZeresPluginLibraryOutdated ? ' and outdated' : ''} `) : (XenoLibOutdated || ZeresPluginLibraryOutdated) && (d += `Outdated `), (d += `${c ? 'Libraries' : 'Library'} `), d; + })(), + e = (() => { + let d = `The ${c ? 'libraries' : 'library'} `; + return a || XenoLibOutdated ? ((d += 'XenoLib '), (b || ZeresPluginLibraryOutdated) && (d += 'and ZeresPluginLibrary ')) : (b || ZeresPluginLibraryOutdated) && (d += 'ZeresPluginLibrary '), (d += `required for ${this.name} ${c ? 'are' : 'is'} ${a || b ? 'missing' : ''}${XenoLibOutdated || ZeresPluginLibraryOutdated ? (a || b ? ' and/or outdated' : 'outdated') : ''}.`), d; + })(), + f = BdApi.findModuleByProps('push', 'update', 'pop', 'popWithKey'), + g = BdApi.findModuleByProps('Sizes', 'Weights'), + h = BdApi.findModule(a => a.defaultProps && a.key && 'confirm-modal' === a.key()), + i = () => BdApi.getCore().alert(d, `${e}
Due to a slight mishap however, you'll have to download the libraries yourself. After opening the links, do CTRL + S to download the library.
${b || ZeresPluginLibraryOutdated ? '
Click here to download ZeresPluginLibrary' : ''}${a || XenoLibOutdated ? '
Click here to download XenoLib' : ''}`); + if (!f || !h || !g) return i(); + let j; + const k = (() => { + if (!global.pluginModule || !global.BDEvents) return; + if (a || XenoLibOutdated) { + const a = () => { + BDEvents.off('xenolib-loaded', a), f.popWithKey(j), pluginModule.reloadPlugin(this.name); + }; + return BDEvents.on('xenolib-loaded', a), () => BDEvents.off('xenolib-loaded', a); + } + const b = a => { + 'ZeresPluginLibrary' !== a || (BDEvents.off('plugin-loaded', b), BDEvents.off('plugin-reloaded', b), f.popWithKey(j), pluginModule.reloadPlugin(this.name)); + }; + return BDEvents.on('plugin-loaded', b), BDEvents.on('plugin-reloaded', b), () => (BDEvents.off('plugin-loaded', b), BDEvents.off('plugin-reloaded', b)); + })(); + class l extends BdApi.React.PureComponent { + constructor(a) { + super(a), (this.state = { hasError: !1 }); + } + componentDidCatch(a, b) { + console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`), this.setState({ hasError: !0 }), 'function' == typeof this.props.onError && this.props.onError(a); + } + render() { + return this.state.hasError ? null : this.props.children; + } + } + j = f.push(a => + BdApi.React.createElement( + l, + { + label: 'missing dependency modal', + onError: () => { + f.popWithKey(j), i(); + } + }, + BdApi.React.createElement( + h, + Object.assign( + { + header: d, + children: [BdApi.React.createElement(g, { color: g.Colors.PRIMARY, children: [`${e} Please click Download Now to download ${c ? 'them' : 'it'}.`] })], + red: !1, + confirmText: 'Download Now', + cancelText: 'Cancel', + onConfirm: () => { + k(); + const a = require('request'), + b = require('fs'), + c = require('path'), + d = a => { + if (!global.BDEvents) return a(); + const b = c => { + 'ZeresPluginLibrary' !== c || (BDEvents.off('plugin-loaded', b), BDEvents.off('plugin-reloaded', b), a()); + }; + BDEvents.on('plugin-loaded', b), BDEvents.on('plugin-reloaded', b); + }, + e = () => { + if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return; + if ((global.XenoLib && !XenoLibOutdated) || !global.BDEvents) return pluginModule.reloadPlugin(this.name); + const a = () => { + BDEvents.off('xenolib-loaded', a), pluginModule.reloadPlugin(this.name); + }; + BDEvents.on('xenolib-loaded', a); + }, + f = () => (global.XenoLib && !XenoLibOutdated ? e() : void a('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (a, d, f) => (a ? i() : void (e(), b.writeFile(c.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), f, () => {}))))); + !global.ZeresPluginLibrary || ZeresPluginLibraryOutdated ? a('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (a, e, g) => (a ? i() : void (d(f), b.writeFile(c.join(window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), g, () => {})))) : f(); + } + }, + a + ) ) - ); - }); + ) + ); } start() {}