import { openModal, closeModal } from '@vizality/modal'; import { getModule, contextMenu } from '@vizality/webpack'; import { SettingsContextMenu } from '@vizality/components/vizality'; import { Content, Layout } from '@vizality/components/dashboard'; import { toPlural } from '@vizality/util/string'; import { Confirm, ContextMenu, Modal } from '@vizality/components'; import { joinClassNames, waitForElement } 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 { webFrame } from 'electron' import SettingsPage from './components/Settings'; /** * 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 => ( ) }) ); vizality.api.settings._registerBuiltinSection({ header: 'Settings', icon: content.icon, render: props => }); 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 => ( DiscordNative.app.relaunch()} onCancel={closeModal} >
{Messages.VIZALITY_SETTINGS_RESTART}
)); } async waitFor(filter) { const exists = await getModule(filter, true, true) if (exists) return exists return await this.waitFor(filter) } async patchSettingsContextMenu () { const { panels } = await getModule('panels', 'downloadProgressCircle', 'hasNotice', true) const { container } = await getModule('container', 'usernameContainer', 'godlike', true) const { button } = await getModule('button', 'disabled', 'enabled', true) await waitForElement(`.${panels} > .${container} .${button}:last-child`); const settingsNode = webFrame.top.context.document.querySelector(`.${panels} > .${container} .${button}:last-child`); settingsNode.__reactProps$.onContextMenu({ stopPropagation() {}, currentTarget: { contains: () => true }, preventDefault() {} }); contextMenu.closeContextMenu(); const DiscordSettingsContextMenu = await this.waitFor(m => { if (!m.default) return; const string = String(m.default); return string.includes('.AnalyticsLocationProvider,') && string.includes('.createElement(') && !string.includes('return function') && !m. default.displayName; }); patch('vz-settings-context-menu', DiscordSettingsContextMenu, 'default', (_, res) => { const { children } = res.props; const old = children.type; children.type = () => { const result = old(children.props); const items = result.props.children.props.children.find(child => Array.isArray(child)); items.push( <> vizality.api.routes.navigateTo('dashboard')} > {SettingsContextMenu.type().props.children} ); return result; }; }); } 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}')` }; } }); } }