mirror of https://github.com/vizality/vizality
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
8.7 KiB
311 lines
8.7 KiB
import { openModal, closeModal } from '@vizality/modal';
|
|
import { getModuleByDisplayName, getModule, instance } from '@vizality/webpack';
|
|
import { SettingsContextMenu } from '@vizality/components/vizality';
|
|
import { Content, Layout } from '@vizality/components/dashboard';
|
|
import { toPlural, toTitleCase } from '@vizality/util/string';
|
|
import { Confirm, ContextMenu, Modal } from '@vizality/components';
|
|
import { joinClassNames } from '@vizality/util/dom';
|
|
import { patch, unpatch } from '@vizality/patcher';
|
|
import { Builtin } from '@vizality/entities';
|
|
import { Messages } from '@vizality/i18n';
|
|
import React from 'react';
|
|
|
|
import SettingsPage from './components/Settings';
|
|
|
|
/**
|
|
* Contributed by DoggyBootsy, originally written by Strencher.
|
|
*/
|
|
{
|
|
const modules = Object.values(instance.m).filter(e => e.toString().search(/openContextMenuLazy[)(]/) > -1);
|
|
const regex = [
|
|
{
|
|
regex: /return Promise\.all\(\[(.*?)\]\)\.then\(\D+(.+?)\)\)/,
|
|
parse (match) {
|
|
return {
|
|
imports: matchAll(/\(([\de]+)\)/g, match[0]).map(e => Number(e)),
|
|
main: match[1]
|
|
};
|
|
}
|
|
},
|
|
{
|
|
regex: /return Promise\.resolve\(\)\.then\(\D+(.+?)\)/,
|
|
parse (match) {
|
|
return {
|
|
imports: [],
|
|
main: match[0]
|
|
};
|
|
}
|
|
},
|
|
{
|
|
regex: /return [rn]\.e\((\d+)\)\.then\(\D+(\d+)\)\)/,
|
|
parse (match) {
|
|
return {
|
|
imports: [ match[0] ],
|
|
main: match[0]
|
|
};
|
|
}
|
|
}
|
|
];
|
|
|
|
function matchAll (regex, input, parent = false) {
|
|
const output = [];
|
|
let matches,
|
|
lastIndex = 0;
|
|
while (matches = regex.exec(input.slice(lastIndex))) {
|
|
if (!regex.global) lastIndex += matches.index + matches[0].length;
|
|
if (parent) output.push(matches);
|
|
else {
|
|
const [ , ...match ] = matches;
|
|
output.push(match);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
const found = [];
|
|
|
|
for (let i = 0; i < modules.length; i++) {
|
|
const str = modules[i].toString();
|
|
|
|
for (let i2 = 0; i2 < regex.length; i2++) {
|
|
const { regex: reg, parse } = regex[i2];
|
|
if (!reg.test(str)) continue;
|
|
const result = matchAll(reg, str).map(res => ({ ...parse(res), module: modules[i], reg }));
|
|
found.push(...result);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Promise.all(found.map(item => {
|
|
try {
|
|
const imports = item.imports.map(i => instance.e(i));
|
|
return Promise.all(imports).then(instance.bind(instance, item.main)).catch(() => void 0);
|
|
} catch (error) {
|
|
return Promise.resolve();
|
|
}
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* The settings page categories.
|
|
*/
|
|
const categories = [
|
|
'general',
|
|
'account',
|
|
'appearance',
|
|
'notifications',
|
|
'keybinds',
|
|
'commands',
|
|
'developer',
|
|
'advanced'
|
|
];
|
|
|
|
const content = {
|
|
header: 'Settings',
|
|
icon: 'gear'
|
|
};
|
|
|
|
export default class Settings extends Builtin {
|
|
start () {
|
|
this.pie = [];
|
|
this.injectStyles('styles/main.scss');
|
|
/**
|
|
* Register a route for each of the settings categories.
|
|
*/
|
|
categories.forEach(category =>
|
|
vizality.api.routes.registerRoute({
|
|
id: `settings/${category}`,
|
|
path: `/settings/${category}`,
|
|
render: props => (
|
|
<Layout>
|
|
<Content
|
|
header={content.header}
|
|
Separator={false}
|
|
icon={content.icon}
|
|
vz-builtin={this.addonId}
|
|
vz-section={category}
|
|
>
|
|
<SettingsPage
|
|
{...props}
|
|
builtin={this}
|
|
section={category}
|
|
/>
|
|
</Content>
|
|
</Layout>
|
|
)
|
|
})
|
|
);
|
|
|
|
vizality.api.settings._registerBuiltinSection({
|
|
header: 'Settings',
|
|
icon: content.icon,
|
|
render: props => <SettingsPage {...props} builtin={this} section='general' />
|
|
});
|
|
|
|
vizality.api.actions.registerAction('CONFIRM_RESTART', () => this.confirmRestart());
|
|
|
|
this.patchSettingsContextMenu();
|
|
this.patchSettingsContextMenuAddonItem();
|
|
this.patchSettingsContextMenuAddonCheckboxItem();
|
|
}
|
|
|
|
stop () {
|
|
categories.forEach(category => vizality.api.routes.unregisterRoute(`settings/${category}`));
|
|
vizality.api.routes.unregisterRoute('settings');
|
|
vizality.api.actions.unregisterAction('CONFIRM_RESTART');
|
|
unpatch('vz-settings-context-menu');
|
|
unpatch('vz-settings-context-menu-addon-items');
|
|
unpatch('vz-settings-context-menu-addon-checkbox-items');
|
|
}
|
|
|
|
confirmRestart () {
|
|
const { colorStandard } = getModule('colorStandard');
|
|
const { spacing } = getModule('spacing', 'message');
|
|
const { size16 } = getModule('size16');
|
|
|
|
openModal(() => props => (
|
|
<Modal.Root {...props}>
|
|
<Confirm
|
|
{...props}
|
|
red
|
|
header={Messages.ERRORS_RESTART_APP}
|
|
confirmText={Messages.BUNDLE_READY_RESTART}
|
|
cancelText={Messages.BUNDLE_READY_LATER}
|
|
onConfirm={() => DiscordNative.app.relaunch()}
|
|
onCancel={closeModal}
|
|
>
|
|
<div className={joinClassNames(colorStandard, spacing, size16)}>
|
|
{Messages.VIZALITY_SETTINGS_RESTART}
|
|
</div>
|
|
</Confirm>
|
|
</Modal.Root>
|
|
));
|
|
}
|
|
|
|
async patchSettingsContextMenu () {
|
|
const DiscordSettingsContextMenu = await getModule(m => m.default?.displayName === 'UserSettingsCogContextMenu', true);
|
|
patch('vz-settings-context-menu', DiscordSettingsContextMenu, 'default', (_, res) => {
|
|
const items = res.props.children.find(child => Array.isArray(child));
|
|
items.push(
|
|
<>
|
|
<ContextMenu.Separator />
|
|
<ContextMenu.Item
|
|
id='vizality-dashboard'
|
|
label='Vizality'
|
|
action={() => vizality.api.routes.navigateTo('dashboard')}
|
|
>
|
|
{SettingsContextMenu.type().props.children}
|
|
</ContextMenu.Item>
|
|
</>
|
|
);
|
|
});
|
|
}
|
|
|
|
patchSettingsContextMenuAddonCheckboxItem () {
|
|
const MenuCheckboxItem = getModule(m => m.default?.displayName === 'MenuCheckboxItem');
|
|
patch('vz-settings-context-menu-addon-checkbox-items', MenuCheckboxItem, 'default', ([ props ], res) => {
|
|
if (!res.props?.id) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* If the ID doesn't start with one of the items we're targetting, don't patch it.
|
|
*/
|
|
if ((res.props.id.indexOf('vizality-dashboard--plugins--') > 0 &&
|
|
res.props.id.indexOf('vizality-dashboard--themes--') > 0) ||
|
|
res.props['vz-addon-icon']
|
|
) return;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const type =
|
|
new RegExp(/(vizality-dashboard-?-plugins)/).test(res.props.id)
|
|
? 'plugin'
|
|
: new RegExp(/(vizality-dashboard-?-themes)/).test(res.props.id)
|
|
? 'theme'
|
|
: null;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
if (type !== 'plugin' && type !== 'theme') {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const addonId = res.props.id.replace(new RegExp(`(user-settings-cog-)?(vizality-dashboard-?-${toPlural(type)}--)`), '');
|
|
|
|
/**
|
|
*
|
|
*/
|
|
if (!vizality.manager[toPlural(type)].isInstalled(addonId)) {
|
|
return;
|
|
}
|
|
|
|
const addonIconUrl = props['vz-addon-icon'];
|
|
res.props['vz-addon-icon'] = '';
|
|
res.props['vz-addon-id'] = props.id;
|
|
if (addonIconUrl) {
|
|
res.props.style = { ...res.props.style, '--vz-addon-icon': `url('${addonIconUrl}')` };
|
|
}
|
|
});
|
|
}
|
|
|
|
patchSettingsContextMenuAddonItem () {
|
|
const MenuItem = getModule(m => m.default?.displayName === 'MenuItem');
|
|
patch('vz-settings-context-menu-addon-items', MenuItem, 'default', ([ props ], res) => {
|
|
if (!res.props?.id) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* If the ID doesn't start with one of the items we're targetting, don't patch it.
|
|
*/
|
|
if ((res.props.id.indexOf('vizality-dashboard--plugins--') > 0 &&
|
|
res.props.id.indexOf('vizality-dashboard--themes--') > 0) ||
|
|
res.props['vz-addon-icon']
|
|
) return;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const type =
|
|
new RegExp(/(vizality-dashboard-?-plugins)/).test(res.props.id)
|
|
? 'plugin'
|
|
: new RegExp(/(vizality-dashboard-?-themes)/).test(res.props.id)
|
|
? 'theme'
|
|
: null;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
if (type !== 'plugin' && type !== 'theme') {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const addonId = res.props.id.replace(new RegExp(`(user-settings-cog-)?(vizality-dashboard-?-${toPlural(type)}--)`), '');
|
|
|
|
/**
|
|
*
|
|
*/
|
|
if (!vizality.manager[toPlural(type)].isInstalled(addonId)) {
|
|
return;
|
|
}
|
|
|
|
const addonIconUrl = props['vz-addon-icon'];
|
|
res.props['vz-addon-icon'] = '';
|
|
res.props['vz-addon-id'] = props.id;
|
|
if (addonIconUrl) {
|
|
res.props.style = { ...res.props.style, '--vz-addon-icon': `url('${addonIconUrl}')` };
|
|
}
|
|
});
|
|
}
|
|
}
|