STR v2.0.10

This commit is contained in:
_Lighty_ 2020-02-27 21:04:39 +01:00
parent 7b9f56537c
commit b668463c39
2 changed files with 216 additions and 138 deletions

View File

@ -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.

View File

@ -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}<br/>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.<br/>${(zlibMissing && '<br/><a href="https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js"target="_blank">Click here to download ZeresPluginLibrary</a>') || ''}${(zlibMissing && '<br/><a href="http://localhost:7474/XenoLib.js"target="_blank">Click here to download XenoLib</a>') || ''}`);
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}<br/>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.<br/>${b || ZeresPluginLibraryOutdated ? '<br/><a href="http://betterdiscord.net/ghdl/?url=https://github.com/rauenzi/BDPluginLibrary/blob/master/release/0PluginLibrary.plugin.js"target="_blank">Click here to download ZeresPluginLibrary</a>' : ''}${a || XenoLibOutdated ? '<br/><a href="http://betterdiscord.net/ghdl/?url=https://github.com/1Lighty/BetterDiscordPlugins/blob/master/Plugins/1XenoLib.plugin.js"target="_blank">Click here to download XenoLib</a>' : ''}`);
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() {}