STR v2.0.9
This commit is contained in:
parent
6bcfd6f427
commit
0f06bc2d23
|
@ -1,4 +1,9 @@
|
||||||
# [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog
|
# [SaveToRedux](https://1lighty.github.io/BetterDiscordStuff/?plugin=SaveToRedux "SaveToRedux") Changelog
|
||||||
|
### 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.
|
||||||
|
- Properly sanitizing filenames now.
|
||||||
|
|
||||||
### 2.0.8
|
### 2.0.8
|
||||||
- Fixed crash if XenoLib or ZeresPluginLib were missing
|
- Fixed crash if XenoLib or ZeresPluginLib were missing
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ Dynamic options must be wrapped like ${OPTION}
|
||||||
Warn - Always warn if a file with the same name exists
|
Warn - Always warn if a file with the same name exists
|
||||||
Overwrite
|
Overwrite
|
||||||
Append number - appends a number in paranthesis
|
Append number - appends a number in paranthesis
|
||||||
Append random
|
Append random
|
||||||
|
Save As... - lets you enter a custom name instead
|
||||||
#### Misc
|
#### Misc
|
||||||
##### Context menu option at the bottom instead of top
|
##### Context menu option at the bottom instead of top
|
||||||
Force the Save * To option to stay at the bottom at all times
|
Force the Save * To option to stay at the bottom at all times
|
||||||
|
|
|
@ -41,16 +41,16 @@ var SaveToRedux = (() => {
|
||||||
twitter_username: ''
|
twitter_username: ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
version: '2.0.8',
|
version: '2.0.9',
|
||||||
description: 'Allows you to save images, videos, profile icons, server icons, reactions, emotes and custom status emotes to any folder quickly.',
|
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: 'https://github.com/1Lighty',
|
||||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js'
|
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/SaveToRedux/SaveToRedux.plugin.js'
|
||||||
},
|
},
|
||||||
changelog: [
|
changelog: [
|
||||||
{
|
{
|
||||||
title: 'sad',
|
title: 'QOL',
|
||||||
type: 'fixed',
|
type: 'fixed',
|
||||||
items: ['Fixed crash if XenoLib or ZeresPluginLib were missing']
|
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.']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
defaultConfig: [
|
defaultConfig: [
|
||||||
|
@ -89,7 +89,8 @@ var SaveToRedux = (() => {
|
||||||
{ name: 'Warn', value: 0 },
|
{ name: 'Warn', value: 0 },
|
||||||
{ name: 'Overwrite', value: 1 },
|
{ name: 'Overwrite', value: 1 },
|
||||||
{ name: 'Append number: (1)', value: 2 },
|
{ name: 'Append number: (1)', value: 2 },
|
||||||
{ name: 'Append random', value: 3 }
|
{ name: 'Append random', value: 3 },
|
||||||
|
{ name: 'Save as...', value: 4 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ name: 'User and Server icons get saved by the users or servers name, instead of randomized', id: 'saveByName', type: 'switch', value: true },
|
{ name: 'User and Server icons get saved by the users or servers name, instead of randomized', id: 'saveByName', type: 'switch', value: true },
|
||||||
|
@ -107,19 +108,20 @@ var SaveToRedux = (() => {
|
||||||
|
|
||||||
const ContextMenuSubMenuItem = WebpackModules.getByDisplayName('FluxContainer(SubMenuItem)');
|
const ContextMenuSubMenuItem = WebpackModules.getByDisplayName('FluxContainer(SubMenuItem)');
|
||||||
const TextComponent = WebpackModules.getByDisplayName('Text');
|
const TextComponent = WebpackModules.getByDisplayName('Text');
|
||||||
const getEmojiURL = (WebpackModules.getByProps('getEmojiURL') || {}).getEmojiURL;
|
const getEmojiURL = WebpackModules.getByProps('getEmojiURL').getEmojiURL;
|
||||||
const showAlertModal = (WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)) || {}).show;
|
const showAlertModal = WebpackModules.find(m => m.show && m.show.toString().search(/\w\.minorText,\w=\w\.onConfirmSecondary/)).show;
|
||||||
|
|
||||||
const dialog = require('electron').remote.dialog;
|
const dialog = require('electron').remote.dialog;
|
||||||
const openSaveDialog = dialog.showSaveDialogSync || dialog.showSaveDialog;
|
const openSaveDialog = dialog.showSaveDialogSync || dialog.showSaveDialog;
|
||||||
const openOpenDialog = dialog.showOpenDialogSync || dialog.showOpenDialog;
|
const openOpenDialog = dialog.showOpenDialogSync || dialog.showOpenDialog;
|
||||||
const openItem = require('electron').shell.openItem;
|
const openItem = require('electron').shell.openItem;
|
||||||
|
const DelayedCall = WebpackModules.getByProps('DelayedCall').DelayedCall;
|
||||||
const FsModule = require('fs');
|
const FsModule = require('fs');
|
||||||
const RequestModule = require('request');
|
const RequestModule = require('request');
|
||||||
const PathModule = require('path');
|
const PathModule = require('path');
|
||||||
const MimeTypesModule = require('mime-types');
|
const MimeTypesModule = require('mime-types');
|
||||||
const FormItem = WebpackModules.getByDisplayName('FormItem');
|
const FormItem = WebpackModules.getByDisplayName('FormItem');
|
||||||
const Messages = (WebpackModules.getByProps('Messages') || {}).Messages;
|
const Messages = WebpackModules.getByProps('Messages').Messages;
|
||||||
const TextInput = WebpackModules.getByDisplayName('TextInput');
|
const TextInput = WebpackModules.getByDisplayName('TextInput');
|
||||||
const AvatarModule = WebpackModules.getByProps('getChannelIconURL');
|
const AvatarModule = WebpackModules.getByProps('getChannelIconURL');
|
||||||
|
|
||||||
|
@ -218,6 +220,48 @@ var SaveToRedux = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I DO NOT OWN THESE TWO
|
||||||
|
*/
|
||||||
|
function sanitizeFileName(r, e) {
|
||||||
|
// https://github.com/parshap/node-sanitize-filename
|
||||||
|
function n(r, n) {
|
||||||
|
function o(r, e, n) {
|
||||||
|
// https://github.com/parshap/truncate-utf8-bytes
|
||||||
|
function t(r) {
|
||||||
|
return r >= 55296 && 56319 >= r;
|
||||||
|
}
|
||||||
|
function u(r) {
|
||||||
|
return r >= 56320 && 57343 >= r;
|
||||||
|
}
|
||||||
|
if ('string' != typeof e) throw new Error('Input must be string');
|
||||||
|
for (var i, f, c = e.length, o = 0, l = 0; c > l; l += 1) {
|
||||||
|
if (((i = e.charCodeAt(l)), (f = e[l]), t(i) && u(e.charCodeAt(l + 1)) && ((l += 1), (f += e[l])), (o += r(f)), o === n)) return e.slice(0, l + 1);
|
||||||
|
if (o > n) return e.slice(0, l - f.length + 1);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if ('string' != typeof r) throw new Error('Input must be string');
|
||||||
|
var l = Buffer.byteLength.bind(Buffer),
|
||||||
|
a = o.bind(null, l),
|
||||||
|
p = r
|
||||||
|
.replace(t, n)
|
||||||
|
.replace(u, n)
|
||||||
|
.replace(i, n)
|
||||||
|
.replace(f, n)
|
||||||
|
.replace(c, n);
|
||||||
|
return a(p, e.extLength);
|
||||||
|
}
|
||||||
|
var t = /[\/\?<>\\:\*\|"]/g,
|
||||||
|
u = /[\x00-\x1f\x80-\x9f]/g,
|
||||||
|
i = /^\.+$/,
|
||||||
|
f = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i,
|
||||||
|
c = /[\. ]+$/,
|
||||||
|
o = (e && e.replacement) || '',
|
||||||
|
l = n(r, o);
|
||||||
|
return '' === o ? l : n(l, '');
|
||||||
|
}
|
||||||
|
|
||||||
return class SaveToRedux extends Plugin {
|
return class SaveToRedux extends Plugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -261,6 +305,12 @@ var SaveToRedux = (() => {
|
||||||
.ST-modal {
|
.ST-modal {
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
}
|
}
|
||||||
|
.ST-randomize {
|
||||||
|
justify-content: unset;
|
||||||
|
}
|
||||||
|
.ST-randomize > .${XenoLib.getSingleClass('lookBlank contents')} {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
this.lastUsedFolder = -1;
|
this.lastUsedFolder = -1;
|
||||||
|
@ -307,20 +357,26 @@ var SaveToRedux = (() => {
|
||||||
async patchReactions(promiseState) {
|
async patchReactions(promiseState) {
|
||||||
const Reaction = await ReactComponents.getComponentByName('Reaction', `.${XenoLib.getSingleClass('reactionMe reactions')} > div:not(.${XenoLib.getSingleClass('reactionMe reactionBtn')})`);
|
const Reaction = await ReactComponents.getComponentByName('Reaction', `.${XenoLib.getSingleClass('reactionMe reactions')} > div:not(.${XenoLib.getSingleClass('reactionMe reactionBtn')})`);
|
||||||
if (promiseState.cancelled) return;
|
if (promiseState.cancelled) return;
|
||||||
Patcher.after(Reaction.component.prototype, 'render', (_this, _, ret) => {
|
const unpatch = Patcher.after(Reaction.component.prototype, 'render', (_this, _, ret) => {
|
||||||
const oChildren = ret.props.children;
|
const oChildren = ret.props.children;
|
||||||
ret.props.children = e => {
|
ret.props.children = e => {
|
||||||
const oChRet = oChildren(e);
|
try {
|
||||||
const url = _this.props.emoji.id ? getEmojiURL({ id: _this.props.emoji.id, animated: _this.props.emoji.animated }) : WebpackModules.getByProps('getURL').getURL(_this.props.emoji.name);
|
const oChRet = oChildren(e);
|
||||||
XenoLib.createSharedContext(
|
const url = _this.props.emoji.id ? getEmojiURL({ id: _this.props.emoji.id, animated: _this.props.emoji.animated }) : WebpackModules.getByProps('getURL').getURL(_this.props.emoji.name);
|
||||||
() => {
|
XenoLib.createSharedContext(
|
||||||
const submenu = this.constructMenu(url.split('?')[0], 'Reaction', _this.props.emoji.name);
|
() => {
|
||||||
return XenoLib.createContextMenuGroup([submenu]);
|
const submenu = this.constructMenu(url.split('?')[0], 'Reaction', _this.props.emoji.name);
|
||||||
},
|
return XenoLib.createContextMenuGroup([submenu]);
|
||||||
oChRet.props,
|
},
|
||||||
'MESSAGE_REACTIONS'
|
oChRet.props,
|
||||||
);
|
'MESSAGE_REACTIONS'
|
||||||
return oChRet;
|
);
|
||||||
|
return oChRet;
|
||||||
|
} catch (e) {
|
||||||
|
Logger.stacktrace('Error in Reaction patch', e);
|
||||||
|
unpatch(); // for the better..
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
Reaction.forceUpdateAll();
|
Reaction.forceUpdateAll();
|
||||||
|
@ -346,7 +402,7 @@ var SaveToRedux = (() => {
|
||||||
return DiscordAPI.currentChannel.members.reduce((p, c) => (p ? `${p}, ${c.username}` : c.username), '');
|
return DiscordAPI.currentChannel.members.reduce((p, c) => (p ? `${p}, ${c.username}` : c.username), '');
|
||||||
}
|
}
|
||||||
|
|
||||||
formatFilename(name, previewDate, previewRand) {
|
formatFilename(name, previewDate, previewRand, extension, throwFail) {
|
||||||
const date = previewDate || new Date();
|
const date = previewDate || new Date();
|
||||||
const rand = previewRand || this.rand();
|
const rand = previewRand || this.rand();
|
||||||
let ret = 'INTERNAL_ERROR';
|
let ret = 'INTERNAL_ERROR';
|
||||||
|
@ -368,7 +424,7 @@ var SaveToRedux = (() => {
|
||||||
break;
|
break;
|
||||||
case 4: // custom
|
case 4: // custom
|
||||||
// options file rand date time day month year hours minutes seconds name
|
// options file rand date time day month year hours minutes seconds name
|
||||||
return Utilities.formatTString(this.settings.saveOptions.customFileName, {
|
ret = Utilities.formatTString(this.settings.saveOptions.customFileName, {
|
||||||
rand,
|
rand,
|
||||||
file: name,
|
file: name,
|
||||||
date: date
|
date: date
|
||||||
|
@ -376,8 +432,8 @@ var SaveToRedux = (() => {
|
||||||
.split('/')
|
.split('/')
|
||||||
.join('-'),
|
.join('-'),
|
||||||
time: `${date.getMinutes()}-${date.getSeconds()}-${date.getMilliseconds()}`,
|
time: `${date.getMinutes()}-${date.getSeconds()}-${date.getMilliseconds()}`,
|
||||||
day: date.getDay(),
|
day: date.getDate(), // note to self: getDate gives you the day of month
|
||||||
month: date.getMonth(),
|
month: date.getMonth() + 1, // getMonth gives 0-11
|
||||||
year: date.getFullYear(),
|
year: date.getFullYear(),
|
||||||
hours: date.getHours(),
|
hours: date.getHours(),
|
||||||
minutes: date.getMinutes(),
|
minutes: date.getMinutes(),
|
||||||
|
@ -385,17 +441,19 @@ var SaveToRedux = (() => {
|
||||||
name: this.getLocationName()
|
name: this.getLocationName()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.settings.saveOptions.appendCurrentName && (DiscordAPI.currentGuild || DiscordAPI.currentChannel)) {
|
if (this.settings.saveOptions.fileNameType !== 4) {
|
||||||
const name = this.getLocationName();
|
if (this.settings.saveOptions.appendCurrentName && (DiscordAPI.currentGuild || DiscordAPI.currentChannel)) {
|
||||||
if (name) ret += `-${name}`;
|
const name = this.getLocationName();
|
||||||
|
if (name) ret += `-${name}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ret = sanitizeFileName(ret, { extLength: extension ? 255 - (extension.length + 1) : 255 });
|
||||||
|
if (!ret.length && throwFail) throw 'CUST_ERROR_1';
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatURL(url, requiresSize, customName, fallbackExtension, proxiedUrl) {
|
formatURL(url, requiresSize, customName, fallbackExtension, proxiedUrl) {
|
||||||
// url = url.replace(/\/$/, '');
|
// url = url.replace(/\/$/, '');
|
||||||
if (url.indexOf('/a_') !== -1) url = url.replace('.webp', '.gif').replace('.png', '.gif');
|
|
||||||
else url = url.replace('.webp', '.png');
|
|
||||||
if (requiresSize) url += '?size=2048';
|
if (requiresSize) url += '?size=2048';
|
||||||
const match = url.match(/(?:\/)([^\/]+?)(?:(?:\.)([^.\/?:]+)){0,1}(?:[^\w\/\.]+\w+){0,1}(?:(?:\?[^\/]+){0,1}|(?:\/){0,1})$/);
|
const match = url.match(/(?:\/)([^\/]+?)(?:(?:\.)([^.\/?:]+)){0,1}(?:[^\w\/\.]+\w+){0,1}(?:(?:\?[^\/]+){0,1}|(?:\/){0,1})$/);
|
||||||
let name = customName || match[1];
|
let name = customName || match[1];
|
||||||
|
@ -404,9 +462,15 @@ var SaveToRedux = (() => {
|
||||||
extension = name;
|
extension = name;
|
||||||
name = url.match(/\/\/media.tenor.co\/[^\/]+\/([^\/]+)\//)[1];
|
name = url.match(/\/\/media.tenor.co\/[^\/]+\/([^\/]+)\//)[1];
|
||||||
} else if (url.indexOf('//i.giphy.com/media/') !== -1) name = url.match(/\/\/i\.giphy\.com\/media\/([^\/]+)\//)[1];
|
} else if (url.indexOf('//i.giphy.com/media/') !== -1) name = url.match(/\/\/i\.giphy\.com\/media\/([^\/]+)\//)[1];
|
||||||
name = this.formatFilename(name);
|
let forceSaveAs = false;
|
||||||
|
try {
|
||||||
|
name = this.formatFilename(name, undefined, undefined, extension, true);
|
||||||
|
} catch (e) {
|
||||||
|
if (e !== 'CUST_ERROR_1') throw e;
|
||||||
|
forceSaveAs = true;
|
||||||
|
}
|
||||||
const isTrusted = isTrustedDomain(url);
|
const isTrusted = isTrustedDomain(url);
|
||||||
const ret = { fileName: (extension && `${name}.${extension}`) || name, url: isTrusted ? url : proxiedUrl || url, name, extension, untrusted: !isTrusted && !proxiedUrl };
|
const ret = { fileName: (extension && `${name}.${extension}`) || name, url: isTrusted ? url : proxiedUrl || url, name, extension, untrusted: !isTrusted && !proxiedUrl, forceSaveAs };
|
||||||
// Logger.info(`[formatURL] url \`${url}\` requiresSize \`${requiresSize}\` customName \`${customName}\`, ret ${JSON.stringify(ret, '', 1)}`);
|
// Logger.info(`[formatURL] url \`${url}\` requiresSize \`${requiresSize}\` customName \`${customName}\`, ret ${JSON.stringify(ret, '', 1)}`);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -460,12 +524,83 @@ var SaveToRedux = (() => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveFile = (path, basePath, openOnSave, dontWarn) => {
|
const saveAs = (folder, onOk) => {
|
||||||
|
let val = formattedurl.name;
|
||||||
|
let inputRef = null;
|
||||||
|
let delayedCall = new DelayedCall(350, () => {
|
||||||
|
inputRef.props.value = val = sanitizeFileName(val, 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0));
|
||||||
|
inputRef.forceUpdate();
|
||||||
|
});
|
||||||
|
Modals.showModal(
|
||||||
|
'Save as...',
|
||||||
|
React.createElement(
|
||||||
|
FormItem,
|
||||||
|
{
|
||||||
|
title: 'Name your file'
|
||||||
|
},
|
||||||
|
React.createElement(TextInput, {
|
||||||
|
maxLength: 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0),
|
||||||
|
ref: e => (inputRef = e),
|
||||||
|
value: val,
|
||||||
|
onChange: e => {
|
||||||
|
val = e;
|
||||||
|
inputRef.props.value = val;
|
||||||
|
if (val.trim() !== sanitizeFileName(val.trim(), 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0))) inputRef.props.error = 'Invalid characters in name';
|
||||||
|
else inputRef.props.error = undefined;
|
||||||
|
inputRef.forceUpdate();
|
||||||
|
},
|
||||||
|
placeholder: formattedurl.name,
|
||||||
|
autoFocus: true,
|
||||||
|
error: !folder ? 'Invalid filename, please set a name' : folder === -1 ? 'File already exists, try another name' : undefined
|
||||||
|
}),
|
||||||
|
React.createElement(XenoLib.ReactComponents.Button, {
|
||||||
|
children: 'Randomize',
|
||||||
|
className: XenoLib.joinClassNames(DiscordClasses.Margins.marginBottom20.value, XenoLib.getClass('input reset'), 'ST-randomize'),
|
||||||
|
color: XenoLib.ReactComponents.Button.Colors.PRIMARY,
|
||||||
|
look: XenoLib.ReactComponents.ButtonOptions.ButtonLooks.LINK,
|
||||||
|
onClick: () => {
|
||||||
|
val = this.rand();
|
||||||
|
inputRef.props.value = val;
|
||||||
|
inputRef.forceUpdate();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{
|
||||||
|
confirmText: 'Save',
|
||||||
|
onCancel: e => console.log('oof'),
|
||||||
|
onConfirm: () => {
|
||||||
|
const onDoShitOrWhateverFuckThisShitMan = val => {
|
||||||
|
if (!folder || folder === -1) return onOk(val);
|
||||||
|
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
|
||||||
|
if (!val.length) val = formattedurl.name;
|
||||||
|
else formattedurl.name = val;
|
||||||
|
saveFile(folder.path + `/${val}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, folder.path, false, false);
|
||||||
|
};
|
||||||
|
const sanitized = sanitizeFileName(val, 255 - (formattedurl.extension ? formattedurl.extension.length + 1 : 0));
|
||||||
|
if (val !== sanitized) {
|
||||||
|
if (!sanitized.length) return saveAs(undefined, onOk);
|
||||||
|
return Modals.showConfirmationModal('Invalid characters', `There are invalid characters in the filename. Do you want to strip them? Resulting filename will be ${sanitized}`, {
|
||||||
|
onConfirm: () => {
|
||||||
|
onOk(sanitized);
|
||||||
|
onDoShitOrWhateverFuckThisShitMan(sanitized);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onDoShitOrWhateverFuckThisShitMan(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveFile = (path, basePath, openOnSave, dontWarn, resolved) => {
|
||||||
try {
|
try {
|
||||||
FsModule.accessSync(PathModule.dirname(path), FsModule.constants.W_OK);
|
FsModule.accessSync(PathModule.dirname(path), FsModule.constants.W_OK);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
Logger.stacktrace('Failed to save to folder', err);
|
||||||
return BdApi.showToast(`Error saving to folder: ${err.message.match(/.*: (.*), access '/)[1]}`, { type: 'error' });
|
return BdApi.showToast(`Error saving to folder: ${err.message.match(/.*: (.*), access '/)[1]}`, { type: 'error' });
|
||||||
}
|
}
|
||||||
|
const handleSaveAs = invFilename => saveAs(invFilename ? -1 : undefined, fileName => ((formattedurl.forceSaveAs = false), saveFile(`${basePath}/${fileName}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, basePath, openOnSave, dontWarn, true)));
|
||||||
|
if (formattedurl.forceSaveAs && !resolved) return handleSaveAs();
|
||||||
if (!dontWarn && FsModule.existsSync(path)) {
|
if (!dontWarn && FsModule.existsSync(path)) {
|
||||||
const handleConflict = mode => {
|
const handleConflict = mode => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
@ -485,10 +620,12 @@ var SaveToRedux = (() => {
|
||||||
case 3: {
|
case 3: {
|
||||||
path = `${basePath}/${formattedurl.name}-${this.rand()}${formattedurl.extension ? '.' + formattedurl.extension : ''}`;
|
path = `${basePath}/${formattedurl.name}-${this.rand()}${formattedurl.extension ? '.' + formattedurl.extension : ''}`;
|
||||||
}
|
}
|
||||||
|
case 4:
|
||||||
|
return handleSaveAs(true);
|
||||||
}
|
}
|
||||||
download(path, openOnSave);
|
download(path, openOnSave);
|
||||||
};
|
};
|
||||||
if (this.settings.saveOptions.conflictingFilesHandle) {
|
if (this.settings.saveOptions.conflictingFilesHandle && !formattedurl.forceSaveAs) {
|
||||||
handleConflict(this.settings.saveOptions.conflictingFilesHandle);
|
handleConflict(this.settings.saveOptions.conflictingFilesHandle);
|
||||||
} else {
|
} else {
|
||||||
let ref1, ref2;
|
let ref1, ref2;
|
||||||
|
@ -518,6 +655,10 @@ var SaveToRedux = (() => {
|
||||||
{
|
{
|
||||||
name: 'Append random',
|
name: 'Append random',
|
||||||
value: 3
|
value: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Save as...',
|
||||||
|
value: 4
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
value: 1,
|
value: 1,
|
||||||
|
@ -578,44 +719,7 @@ var SaveToRedux = (() => {
|
||||||
const path = folder.path + `/${formattedurl.fileName}`;
|
const path = folder.path + `/${formattedurl.fileName}`;
|
||||||
saveFile(path, folder.path);
|
saveFile(path, folder.path);
|
||||||
}),
|
}),
|
||||||
XenoLib.createContextMenuItem('Save As...', () => {
|
XenoLib.createContextMenuItem('Save As...', () => saveAs(folder)),
|
||||||
let val = '';
|
|
||||||
let inputRef = null;
|
|
||||||
Modals.showModal(
|
|
||||||
'Save as...',
|
|
||||||
React.createElement(
|
|
||||||
FormItem,
|
|
||||||
{
|
|
||||||
className: DiscordClasses.Margins.marginBottom20,
|
|
||||||
title: 'Name your file'
|
|
||||||
},
|
|
||||||
React.createElement(TextInput, {
|
|
||||||
maxLength: DiscordConstants.MAX_GUILD_FOLDER_NAME_LENGTH,
|
|
||||||
ref: e => (inputRef = e),
|
|
||||||
value: val,
|
|
||||||
onChange: e => {
|
|
||||||
val = e;
|
|
||||||
inputRef.props.value = e;
|
|
||||||
inputRef.forceUpdate();
|
|
||||||
},
|
|
||||||
placeholder: formattedurl.name,
|
|
||||||
autoFocus: true
|
|
||||||
})
|
|
||||||
),
|
|
||||||
{
|
|
||||||
confirmText: 'Save',
|
|
||||||
onConfirm: () => {
|
|
||||||
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
|
|
||||||
if (!val.length) val = formattedurl.name;
|
|
||||||
else formattedurl.name = val;
|
|
||||||
saveFile(folder.path + `/${val}${formattedurl.extension ? '.' + formattedurl.extension : ''}`, folder.path, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/* const path = this.openSaveDialog({ defaultPath: folder.path + `/${formattedurl.fileName}` });
|
|
||||||
if (!path) return BdApi.showToast('Maybe next time.');
|
|
||||||
saveFile(path, undefined, false, true); */
|
|
||||||
}),
|
|
||||||
XenoLib.createContextMenuItem('Save And Open', () => {
|
XenoLib.createContextMenuItem('Save And Open', () => {
|
||||||
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
|
this.lastUsedFolder = this.folders.findIndex(m => m === folder);
|
||||||
const path = folder.path + `/${formattedurl.fileName}`;
|
const path = folder.path + `/${formattedurl.fileName}`;
|
||||||
|
@ -731,6 +835,10 @@ var SaveToRedux = (() => {
|
||||||
const isImage = e => /\.{0,1}(png|jpe?g|webp|gif|svg)$/i.test(e);
|
const isImage = e => /\.{0,1}(png|jpe?g|webp|gif|svg)$/i.test(e);
|
||||||
const isVideo = e => /\.{0,1}(mp4|webm|mov)$/i.test(e);
|
const isVideo = e => /\.{0,1}(mp4|webm|mov)$/i.test(e);
|
||||||
const isAudio = e => /\.{0,1}(mp3|ogg|wav|flac)$/i.test(e);
|
const isAudio = e => /\.{0,1}(mp3|ogg|wav|flac)$/i.test(e);
|
||||||
|
const useCorrectShit = () => {
|
||||||
|
if (url.indexOf('/a_') !== -1) url = url.replace('.webp', '.gif').replace('.png', '.gif');
|
||||||
|
else url = url.replace('.webp', '.png');
|
||||||
|
};
|
||||||
if (type === 'NATIVE_IMAGE' || type === 'MESSAGE_MAIN') {
|
if (type === 'NATIVE_IMAGE' || type === 'MESSAGE_MAIN') {
|
||||||
let src;
|
let src;
|
||||||
if (type === 'NATIVE_IMAGE') {
|
if (type === 'NATIVE_IMAGE') {
|
||||||
|
@ -817,6 +925,7 @@ var SaveToRedux = (() => {
|
||||||
url = _this.props.guild.getIconURL();
|
url = _this.props.guild.getIconURL();
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
if (this.settings.saveOptions.saveByName) customName = _this.props.guild.name;
|
if (this.settings.saveOptions.saveByName) customName = _this.props.guild.name;
|
||||||
|
useCorrectShit();
|
||||||
} else {
|
} else {
|
||||||
if (_this.props.user && _this.props.user.getAvatarURL) {
|
if (_this.props.user && _this.props.user.getAvatarURL) {
|
||||||
saveType = 'Avatar';
|
saveType = 'Avatar';
|
||||||
|
@ -826,6 +935,7 @@ var SaveToRedux = (() => {
|
||||||
url = AvatarModule.getChannelIconURL(_this.props.channel);
|
url = AvatarModule.getChannelIconURL(_this.props.channel);
|
||||||
saveType = 'Icon';
|
saveType = 'Icon';
|
||||||
} else return /* hurr durr? */;
|
} else return /* hurr durr? */;
|
||||||
|
useCorrectShit();
|
||||||
if (url.startsWith('/assets/')) url = 'https://discordapp.com' + url;
|
if (url.startsWith('/assets/')) url = 'https://discordapp.com' + url;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -914,116 +1024,68 @@ var SaveToRedux = (() => {
|
||||||
}
|
}
|
||||||
stop() {}
|
stop() {}
|
||||||
load() {
|
load() {
|
||||||
const XenoLibMissing = !global.XenoLib;
|
const ezlibMissing = !global.XenoLib;
|
||||||
const zlibMissing = !global.ZeresPluginLibrary;
|
const zlibMissing = !global.ZeresPluginLibrary;
|
||||||
const bothLibsMissing = XenoLibMissing && zlibMissing;
|
const bothLibsMissing = ezlibMissing && zlibMissing;
|
||||||
const header = `Missing ${(bothLibsMissing && 'Libraries') || 'Library'}`;
|
const header = `Missing ${(bothLibsMissing && 'Libraries') || 'Library'}`;
|
||||||
const content = `The ${(bothLibsMissing && 'Libraries') || 'Library'} ${(zlibMissing && 'ZeresPluginLibrary') || ''} ${(XenoLibMissing && (zlibMissing ? 'and XenoLib' : 'XenoLib')) || ''} required for ${this.name} ${(bothLibsMissing && 'are') || 'is'} missing.`;
|
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 ModalStack = BdApi.findModuleByProps('push', 'update', 'pop', 'popWithKey');
|
||||||
const TextElement = BdApi.findModuleByProps('Sizes', 'Weights');
|
const TextElement = BdApi.findModuleByProps('Sizes', 'Weights');
|
||||||
const ConfirmationModal = BdApi.findModule(m => m.defaultProps && m.key && m.key() === 'confirm-modal');
|
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>') || ''}`);
|
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();
|
if (!ModalStack || !ConfirmationModal || !TextElement) return onFail();
|
||||||
class TempErrorBoundary extends BdApi.React.PureComponent {
|
ModalStack.push(props => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = { hasError: false };
|
|
||||||
}
|
|
||||||
componentDidCatch(err, inf) {
|
|
||||||
console.error(`Error in ${this.props.label}, screenshot or copy paste the error above to Lighty for help.`);
|
|
||||||
this.setState({ hasError: true });
|
|
||||||
if (typeof this.props.onError === 'function') this.props.onError(err);
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
if (this.state.hasError) return null;
|
|
||||||
return this.props.children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let modalId;
|
|
||||||
const onHeckWouldYouLookAtThat = (() => {
|
|
||||||
if (!global.pluginModule || !global.BDEvents) return;
|
|
||||||
if (XenoLibMissing) {
|
|
||||||
const listener = () => {
|
|
||||||
BDEvents.off('xenolib-loaded', listener);
|
|
||||||
ModalStack.popWithKey(modalId); /* make it easier on the user */
|
|
||||||
pluginModule.reloadPlugin(this.name);
|
|
||||||
};
|
|
||||||
BDEvents.on('xenolib-loaded', listener);
|
|
||||||
return () => BDEvents.off('xenolib-loaded', listener);
|
|
||||||
} else {
|
|
||||||
const onLoaded = e => {
|
|
||||||
if (e !== 'ZeresPluginLibrary') return;
|
|
||||||
BDEvents.off('plugin-loaded', onLoaded);
|
|
||||||
ModalStack.popWithKey(modalId); /* make it easier on the user */
|
|
||||||
pluginModule.reloadPlugin(this.name);
|
|
||||||
};
|
|
||||||
BDEvents.on('plugin-loaded', onLoaded);
|
|
||||||
return () => BDEvents.off('plugin-loaded', onLoaded);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
modalId = ModalStack.push(props => {
|
|
||||||
return BdApi.React.createElement(
|
return BdApi.React.createElement(
|
||||||
TempErrorBoundary,
|
ConfirmationModal,
|
||||||
{
|
Object.assign(
|
||||||
label: 'missing dependency modal',
|
{
|
||||||
onError: () => {
|
header,
|
||||||
ModalStack.popWithKey(modalId); /* smh... */
|
children: [BdApi.React.createElement(TextElement, { color: TextElement.Colors.PRIMARY, children: [`${content} Please click Download Now to install ${(bothLibsMissing && 'them') || 'it'}.`] })],
|
||||||
onFail();
|
red: false,
|
||||||
}
|
confirmText: 'Download Now',
|
||||||
},
|
cancelText: 'Cancel',
|
||||||
BdApi.React.createElement(
|
onConfirm: () => {
|
||||||
ConfirmationModal,
|
const request = require('request');
|
||||||
Object.assign(
|
const fs = require('fs');
|
||||||
{
|
const path = require('path');
|
||||||
header,
|
const waitForLibLoad = callback => {
|
||||||
children: [BdApi.React.createElement(TextElement, { color: TextElement.Colors.PRIMARY, children: [`${content} Please click Download Now to install ${(bothLibsMissing && 'them') || 'it'}.`] })],
|
if (!global.BDEvents) return callback();
|
||||||
red: false,
|
const onLoaded = e => {
|
||||||
confirmText: 'Download Now',
|
if (e !== 'ZeresPluginLibrary') return;
|
||||||
cancelText: 'Cancel',
|
BDEvents.off('plugin-loaded', onLoaded);
|
||||||
onConfirm: () => {
|
callback();
|
||||||
onHeckWouldYouLookAtThat();
|
};
|
||||||
const request = require('request');
|
BDEvents.on('plugin-loaded', onLoaded);
|
||||||
const fs = require('fs');
|
};
|
||||||
const path = require('path');
|
const onDone = () => {
|
||||||
const waitForLibLoad = callback => {
|
if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return;
|
||||||
if (!global.BDEvents) return callback();
|
if (!global.BDEvents || global.XenoLib) pluginModule.reloadPlugin(this.name);
|
||||||
const onLoaded = e => {
|
else {
|
||||||
if (e !== 'ZeresPluginLibrary') return;
|
const listener = () => {
|
||||||
BDEvents.off('plugin-loaded', onLoaded);
|
pluginModule.reloadPlugin(this.name);
|
||||||
callback();
|
BDEvents.off('xenolib-loaded', listener);
|
||||||
};
|
};
|
||||||
BDEvents.on('plugin-loaded', onLoaded);
|
BDEvents.on('xenolib-loaded', listener);
|
||||||
};
|
}
|
||||||
const onDone = () => {
|
};
|
||||||
if (!global.pluginModule || (!global.BDEvents && !global.XenoLib)) return;
|
const downloadXenoLib = () => {
|
||||||
if (!global.BDEvents || global.XenoLib) pluginModule.reloadPlugin(this.name);
|
if (global.XenoLib) return onDone();
|
||||||
else {
|
request('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (error, response, body) => {
|
||||||
const listener = () => {
|
if (error) return onFail();
|
||||||
BDEvents.off('xenolib-loaded', listener);
|
onDone();
|
||||||
pluginModule.reloadPlugin(this.name);
|
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), body, () => {});
|
||||||
};
|
});
|
||||||
BDEvents.on('xenolib-loaded', listener);
|
};
|
||||||
}
|
if (!global.ZeresPluginLibrary) {
|
||||||
};
|
request('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (error, response, body) => {
|
||||||
const downloadXenoLib = () => {
|
if (error) return onFail();
|
||||||
if (global.XenoLib) return onDone();
|
waitForLibLoad(downloadXenoLib);
|
||||||
request('https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js', (error, response, body) => {
|
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '0PluginLibrary.plugin.js'), body, () => {});
|
||||||
if (error) return onFail();
|
});
|
||||||
onDone();
|
} else downloadXenoLib();
|
||||||
fs.writeFile(path.join(window.ContentManager.pluginsFolder, '1XenoLib.plugin.js'), body, () => {});
|
}
|
||||||
});
|
},
|
||||||
};
|
props
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue