rework Vizality settings (new UI and format); wip

pull/67/head
dperolio 3 years ago
parent 2b935362e7
commit f1a2225132
No known key found for this signature in database
GPG Key ID: 3E9BBAA710D3DDCE

@ -0,0 +1,43 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW,
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC}
</Highlight>
}
value={true}
onChange={() => void 0}
info={Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_INFO.format()}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,94 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const [ smoothScrolling, setSmoothScrolling ] = vizality.api.settings.useSetting('smoothScrolling', true);
const [ debugLogs, setDebugLogs ] = vizality.api.settings.useSetting('debugLogs', false);
const [ hideToken, setHideToken ] = vizality.api.settings.useSetting('hideToken', true);
const [ experimentalWebPlatform, setExperimentalWebPlatform ] = vizality.api.settings.useSetting('experimentalWebPlatform', false);
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING,
Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING_DESC,
'pie'
],
render: query =>
<SwitchItem
description={<Highlight search={query}>{Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING_DESC}</Highlight>}
value={smoothScrolling}
onChange={() => setSmoothScrolling(!smoothScrolling)}
help={Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING_HELP}
requiresRestart
>
{Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING}
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_DEBUG_LOGS_DESC,
Messages.VIZALITY_SETTINGS_DEBUG_LOGS,
'pie'
],
render: query =>
<SwitchItem
description={<Highlight search={query}>{Messages.VIZALITY_SETTINGS_DEBUG_LOGS_DESC}</Highlight>}
value={debugLogs}
onChange={() => setDebugLogs(!debugLogs)}
requiresRestart
>
{Messages.VIZALITY_SETTINGS_DEBUG_LOGS}
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_KEEP_TOKEN_DESC,
Messages.VIZALITY_SETTINGS_KEEP_TOKEN,
'pie'
],
render: query =>
<SwitchItem
description={<Highlight search={query}>{Messages.VIZALITY_SETTINGS_KEEP_TOKEN_DESC}</Highlight>}
value={hideToken}
onChange={() => setHideToken(!hideToken)}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_KEEP_TOKEN}
</Highlight>
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM_DESC,
Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM,
'pie'
],
render: query =>
<SwitchItem
description={<Highlight search={query}>{Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM_DESC}</Highlight>}
value={experimentalWebPlatform}
onChange={() => setExperimentalWebPlatform(!experimentalWebPlatform)}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,43 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW,
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC}
</Highlight>
}
value={true}
onChange={() => void 0}
info={Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_INFO.format()}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,71 @@
import { TextInput, SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
/**
* Commands settings options.
* @component
* @returns {React.MemoExoticComponent<function(): React.ReactElement>}
*/
export default memo(({ builtin, search = '' }) => {
const [ commandPrefix, setCommandPrefix ] = vizality.api.settings.useSetting('commandPrefix', '.');
const [ replaceClyde, setReplaceClyde ] = vizality.api.settings.useSetting('replaceClyde', true);
const items = [
{
search: [
Messages.VIZALITY_COMMAND_PREFIX
],
render: query =>
<TextInput
defaultValue={commandPrefix}
onChange={p => setCommandPrefix(!p ? '.' : p.replace(/\s+(?=\S)|(?<=\s)\s+/g, '').toLowerCase())}
onBlur={({ target }) => target.value = commandPrefix}
error={commandPrefix === '/' ? 'Prefix should not be set to `/` as it is already in use by Discord and may disable Vizality autocompletions.' : ''}
>
<Highlight search={query}>
{Messages.VIZALITY_COMMAND_PREFIX}
</Highlight>
</TextInput>
},
{
search: [
Messages.VIZALITY_SETTINGS_NO_CLYDE,
Messages.VIZALITY_SETTINGS_NO_CLYDE_DESC.format({
discordiaUrl: 'https://discordia.me/clyde',
apiUrl: `${window.location.origin}/vizality/docs`
})
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_NO_CLYDE_DESC.format({
discordiaUrl: 'https://discordia.me/clyde',
apiUrl: `${window.location.origin}/vizality/docs`
})}
</Highlight>
}
value={replaceClyde}
onChange={() => setReplaceClyde(!replaceClyde)}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_NO_CLYDE}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,112 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { getModule } from '@vizality/webpack';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const [ reactDeveloperTools, setReactDeveloperTools ] = vizality.api.settings.useSetting('reactDeveloperTools', false);
const [ hotReload, setHotReload ] = vizality.api.settings.useSetting('hotReload', false);
const [ openOverlayDevTools, setOpenOverlayDevTools ] = vizality.api.settings.useSetting('openOverlayDevTools', false);
const [ discordExperiments, setDiscordExperiments ] = vizality.api.settings.useSetting('discordExperiments', false);
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS,
Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS_DESC
],
render: query =>
<SwitchItem
description={<Highlight search={query}>{Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS_DESC}</Highlight>}
value={reactDeveloperTools}
onChange={() => setReactDeveloperTools(!reactDeveloperTools)}
info={Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS_WARNING}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS}
</Highlight>
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_PLUGIN_HOT_RELOAD,
Messages.VIZALITY_SETTINGS_PLUGIN_HOT_RELOAD_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_PLUGIN_HOT_RELOAD_DESC}
</Highlight>
}
value={hotReload}
onChange={async () => {
setHotReload(!hotReload);
await vizality.manager.plugins.remountAll();
}}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_PLUGIN_HOT_RELOAD}
</Highlight>
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_OVERLAY,
Messages.VIZALITY_SETTINGS_OVERLAY_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_OVERLAY_DESC}
</Highlight>
}
value={openOverlayDevTools}
onChange={() => setOpenOverlayDevTools(!openOverlayDevTools)}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_OVERLAY}
</Highlight>
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS,
Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS_DESC.format()
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS_DESC.format()}
</Highlight>
}
value={discordExperiments}
onChange={() => {
setDiscordExperiments(!discordExperiments);
const experimentsModule = getModule(r => r.isDeveloper !== void 0);
experimentsModule._changeCallbacks.forEach(cb => cb());
}}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,296 @@
import { SwitchItem, ButtonItem, Category } from '@vizality/components/settings';
import { Clickable, Button, FormNotice } from '@vizality/components';
import { Directories, Repositories } from '@vizality/constants';
import { removeDirRecursive } from '@vizality/util/file';
import React, { memo, useState, useEffect } from 'react';
import { Messages, chosenLocale } from '@vizality/i18n';
import { joinClassNames } from '@vizality/util/dom';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import { getModule } from '@vizality/webpack';
import { readdirSync, existsSync } from 'fs';
import { useFilter } from '@vizality/hooks';
import { clipboard } from 'electron';
import moment from 'moment';
export default memo(({ builtin, search = '' }) => {
const [ copyText, setCopyText ] = useState(Messages.COPY);
const [ pathsRevealed, setPathsRevealed ] = useState();
const [ pluginsRevealed, setPluginsRevealed ] = useState();
const [ transparentWindow, setTransparentWindow ] = vizality.api.settings.useSetting('transparentWindow', false);
const [ isDiscordCacheCleared, setDiscordCacheCleared ] = useState(false);
const [ isVizalityCacheCleared, setVizalityCacheCleared ] = useState(false);
/**
* Clears Discord's cache.
* @returns {void}
*/
const clearDiscordCache = () => {
try {
setDiscordCacheCleared(true);
vizality.native.app.clearCache();
setTimeout(() => {
setDiscordCacheCleared(false);
}, 2500);
} catch (err) {
builtin.error(err);
}
};
/**
* Clears Vizality's cache.
* @returns {void}
*/
const clearVizalityCache = () => {
try {
setVizalityCacheCleared(true);
removeDirRecursive(Directories.CACHE)
.then(() => {
setTimeout(() => {
setVizalityCacheCleared(false);
}, 2500);
});
} catch (err) {
builtin.error(err);
}
};
/**
*
* @returns {void}
*/
const handleDebugInfoCopy = () => {
const plugins = vizality.manager.plugins.getEnabledKeys();
const extract = document.querySelector('.vz-updater-debug-info > code')
.innerText.replace(/([A-Z/ ]+) (?=\s(?!C:\\).*?:)/g, '\n[$1]').replace(/(.*?):\s(.*.+)/g, '$1="$2"').replace(/[ -](\w*(?=.*=))/g, '$1');
setCopyText(Messages.COPIED);
clipboard.writeText(
`\`\`\`ini
# Debugging Information | Result created: ${moment().calendar()}
${extract.substring(0, extract.indexOf('\nPlugins', extract.indexOf('\nPlugins') + 1))}
Plugins="${plugins.join(', ')}"
\`\`\``.replace(/ {6}|n\/a/g, '').replace(/(?![0-9]{1,3}) \/ (?=[0-9]{1,3})/g, '/')
);
setTimeout(() => setCopyText(Messages.COPY), 2500);
};
/**
*
* @note Intentionally left english-only.
* @returns {void}
*/
const renderDebugInfo = () => {
const { getRegisteredExperiments, getExperimentOverrides } = getModule('initialize', 'getExperimentOverrides');
const superProperties = getModule('getSuperPropertiesBase64').getSuperProperties();
const plugins = vizality.manager.plugins.getEnabledKeys();
const experimentOverrides = Object.keys(getExperimentOverrides()).length;
const availableExperiments = Object.keys(getRegisteredExperiments()).length;
const discordPath = process.resourcesPath.slice(0, -10);
const maskPath = (path) => {
path = path.replace(/(?:\/home\/|C:\\Users\\|\/Users\/)([ \w.-]+).*/i, (path, username) => {
const usernameIndex = path.indexOf(username);
return [ path.slice(0, usernameIndex), username.charAt(0) + username.slice(1).replace(/[a-zA-Z]/g, '*'),
path.slice(usernameIndex + username.length) ].join('');
});
return path;
};
const cachedFiles = (existsSync(Directories.CACHE) && readdirSync(Directories.CACHE)
.map(d => readdirSync(`${Directories.CACHE}/${d}`))
.flat().length) || 'n/a';
const createPathReveal = (title, path) =>
<div className='full-column'>
{title}:&#10;<a
onMouseEnter={() => setPathsRevealed(true)}
onMouseLeave={() => setPathsRevealed(false)}
onClick={() => window.DiscordNative.fileManager.showItemInFolder(path)}
>{pathsRevealed ? path : maskPath(path)}</a>
</div>;
return <FormNotice
type={FormNotice.Types.PRIMARY}
body={<div className={ joinClassNames('vz-updater-debug-info', { copied: copyText === Messages.COPIED })}>
<code>
<b>System / Discord</b>
<div className='row'>
<div className='column'>Locale:&#10;{chosenLocale}</div>
<div className='column'>OS:&#10;{(window.platform.os).toString()}</div>
<div className='column'>Architecture:&#10;{superProperties.os_arch}</div>
{process.platform === 'linux' && (
<div className='column'>Distro:&#10;{superProperties.distro || 'n/a'}</div>
)}
<div className='column'>Release Channel:&#10;{superProperties.release_channel}</div>
<div className='column'>App Version:&#10;{superProperties.client_version}</div>
<div className='column'>Build Number:&#10;{superProperties.client_build_number}</div>
<div className='column'>Build ID:&#10;{window.GLOBAL_ENV.SENTRY_TAGS.buildId}</div>
<div className='column'>Experiments:&#10;{experimentOverrides} / {availableExperiments}</div>
</div>
<b>Process Versions</b>
<div className='row'>
<div className='column'>React:&#10;{React.version}</div>
{[ 'electron', 'chrome', 'node' ].map(proc =>
<div className='column'>{proc.charAt(0).toUpperCase() + proc.slice(1)}:&#10;{process.versions[proc]}</div>
)}
</div>
<b>Vizality</b>
<div className='row'>
<div className='column'>Commands:&#10;{vizality.api.commands.getAllCommands().length}</div>
<div className='column'>Settings:&#10;{Object.keys(vizality.api.settings?.store?.getAllSettings()).length}</div>
<div className='column'>Plugins:&#10;{vizality.manager.plugins.getEnabledKeys().length} / {vizality.manager.plugins.count}
</div>
<div className='column'>Themes:&#10;{vizality.manager.themes.getEnabledKeys().length} / {vizality.manager.themes.count}
</div>
<div className='column'>Cached Files:&#10;{cachedFiles}</div>
<div className='column'>APIs:&#10;{vizality.manager.apis._apis.length}</div>
</div>
<b>Git</b>
<div className='row'>
<div className='column'>Upstream:&#10;{vizality.git.upstream.replace(Repositories.VIZALITY, 'Official')}</div>
<div className='column'>Revision:&#10;
<a
href={`https://github.com/${vizality.git.upstream}/commit/${vizality.git.revision}`}
target='_blank'
>
[{vizality.git.revision.substring(0, 7)}]
</a>
</div>
<div className='column'>Branch:&#10;{vizality.git.branch}</div>
<div className='column'>{`Latest:\n${!vizality.manager.builtins.get('updater').settings.get('updates', []).find(update => update.updateId === 'vizality')}`}</div>
</div>
<b>Listings</b>
<div className='row'>
{createPathReveal('Vizality Path', vizality.dir)}
{createPathReveal('Discord Path', discordPath)}
<div className='full-column'>Experiments:&#10;{experimentOverrides ? Object.keys(getExperimentOverrides()).join(', ') : 'n/a'}</div>
<div className='full-column'>
Plugins:&#10;
{(plugins.length > 6 ? `${(pluginsRevealed ? plugins : plugins.slice(0, 6)).join(', ')}` : plugins.join(', ')) || 'n/a'}&nbsp;
{plugins.length > 6 &&
<Clickable tag='a' onClick={() => setPluginsRevealed(!pluginsRevealed)}>
{pluginsRevealed ? 'Show less' : 'Show more'}
</Clickable>}
</div>
</div>
</code>
<Button
size={Button.Sizes.SMALL}
color={copyText === Messages.COPIED ? Button.Colors.GREEN : Button.Colors.BRAND}
onClick={() => handleDebugInfoCopy()}
>
{copyText}
</Button>
</div>}
/>;
};
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW,
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC,
'pizza'
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC}
</Highlight>
}
value={transparentWindow}
onChange={() => setTransparentWindow(!transparentWindow)}
info={Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_INFO.format()}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW}
</Highlight>
</SwitchItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_CACHE_VIZALITY,
Messages.VIZALITY_SETTINGS_CACHE_VIZALITY_DESC,
'cake'
],
render: query =>
<ButtonItem
note={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_CACHE_VIZALITY_DESC}
</Highlight>
}
button={isVizalityCacheCleared ? Messages.VIZALITY_SETTINGS_CACHE_CLEARED : Messages.VIZALITY_SETTINGS_CACHE_VIZALITY}
success={isVizalityCacheCleared}
onClick={() => clearVizalityCache()}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_CACHE_VIZALITY}
</Highlight>
</ButtonItem>
},
{
search: [
Messages.VIZALITY_SETTINGS_CACHE_DISCORD,
Messages.VIZALITY_SETTINGS_CACHE_DISCORD_DESC,
'pie'
],
render: query =>
<ButtonItem
note={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_CACHE_DISCORD_DESC}
</Highlight>
}
button={isDiscordCacheCleared ? Messages.VIZALITY_SETTINGS_CACHE_CLEARED : Messages.VIZALITY_SETTINGS_CACHE_DISCORD}
success={isDiscordCacheCleared}
onClick={() => clearDiscordCache()}
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_CACHE_DISCORD}
</Highlight>
</ButtonItem>
},
{
search: [
Messages.VIZALITY_UPDATES_OPTS_DEBUG,
Messages.VIZALITY_UPDATES_OPTS_DEBUG_DESC,
'pie'
],
render: query =>
<Category
icon='Bug'
title={
<Highlight search={query}>
{Messages.VIZALITY_UPDATES_OPTS_DEBUG}
</Highlight>
}
description={
<Highlight search={query}>
{Messages.VIZALITY_UPDATES_OPTS_DEBUG_DESC}
</Highlight>
}
>
{renderDebugInfo()}
</Category>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,43 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW,
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC}
</Highlight>
}
value={true}
onChange={() => void 0}
info={Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_INFO.format()}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -0,0 +1,43 @@
import { SwitchItem } from '@vizality/components/settings';
import { Highlight } from 'react-highlighter-ts/dist/lib';
import React, { memo, useEffect } from 'react';
import { useFilter } from '@vizality/hooks';
import { Messages } from '@vizality/i18n';
export default memo(({ builtin, search = '' }) => {
const items = [
{
search: [
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW,
Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC
],
render: query =>
<SwitchItem
description={
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_DESC}
</Highlight>
}
value={true}
onChange={() => void 0}
info={Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW_INFO.format()}
requiresRestart
>
<Highlight search={query}>
{Messages.VIZALITY_SETTINGS_TRANSPARENT_WINDOW}
</Highlight>
</SwitchItem>
}
];
const [ query, setQuery, filteredResults ] = useFilter({
keys: [ 'search' ],
data: items
});
useEffect(() => {
setQuery(search);
}, [ search ]);
return filteredResults.map(result => result.render(query));
});

@ -1,179 +1,235 @@
import React, { memo, useState } from 'react';
import { TextInput, SwitchItem, ButtonItem, Category } from '@vizality/components/settings';
import { removeDirRecursive } from '@vizality/util/file';
import { Directories } from '@vizality/constants';
import { Flex, TextBadge, FilterInput, Markdown, Divider } from '@vizality/components';
import { ContentSidebar, Section } from '@vizality/components/dashboard';
import { SelectInput } from '@vizality/components/settings';
import { joinClassNames } from '@vizality/util/dom';
import { unstable_batchedUpdates } from 'react-dom';
import React, { memo, useState, useEffect } from 'react';
import { getModule } from '@vizality/webpack';
import { Icon } from '@vizality/components';
import { Messages } from '@vizality/i18n';
import { Settings } from '@vizality/api';
import { isEqual } from 'lodash';
export default memo(({ getSetting, toggleSetting, updateSetting }) => {
const [ isDiscordCacheCleared, setDiscordCacheCleared ] = useState(false);
const [ isVizalityCacheCleared, setVizalityCacheCleared ] = useState(false);
import Notifications from './Notifications';
import Appearance from './Appearance';
import Developer from './Developer';
import Advanced from './Advanced';
import Keybinds from './Keybinds';
import Commands from './Commands';
import Account from './Account';
import General from './General';
const clearDiscordCache = () => {
setDiscordCacheCleared(true);
vizality.native.app.clearCache().then(() => {
setTimeout(() => {
setDiscordCacheCleared(false);
}, 2500);
});
};
/**
*
* @private
* @component
*/
const Body = memo(({ props, query, section }) => {
if (query?.length) {
return (
<Section header={<Markdown source={`Showing results for "${query}"`} />} icon='Search'>
<General {...props} search={query} />
<Account {...props} search={query} />
<Appearance {...props} search={query} />
<Notifications {...props} search={query} />
<Keybinds {...props} search={query} />
<Commands {...props} search={query} />
<Developer {...props} search={query} />
<Advanced {...props} search={query}/>
</Section>
);
}
switch (section) {
case 'general':
return (
<Section header='General' icon='Science'>
<General {...props} />
</Section>
);
case 'account':
return (
<Section header='Account' icon='Person'>
<Account {...props} />
</Section>
);
case 'appearance':
return (
<Section header='Appearance' icon='Palette'>
<Appearance {...props} />
</Section>
);
case 'notifications':
return (
<Section header='Notifications' icon='Bell'>
<Notifications {...props} />
</Section>
);
case 'keybinds':
return (
<Section header='Keybinds' icon='Keyboard'>
<Keybinds {...props} />
</Section>
);
case 'commands':
return (
<Section header='Commands' icon='ApplicationCommand'>
<Commands {...props} />
</Section>
);
case 'developer':
return (
<Section header='Developer' icon='Robot'>
<Developer {...props} />
</Section>
);
case 'advanced':
return (
<Section header='Advanced' icon='Wrench'>
<Advanced {...props} />
</Section>
);
default:
return (
<Section header='Account' icon='Person'>
<General {...props} />
</Section>
);
}
});
const clearVizalityCache = () => {
setVizalityCacheCleared(true);
removeDirRecursive(Directories.CACHE).then(() => {
setTimeout(() => {
setVizalityCacheCleared(false);
}, 2500);
});
/**
* Base settings page which controls the rendering of the subcategory settings pages.
* @component
* @returns {React.MemoExoticComponent<function(): React.ReactElement>}
*/
export default memo(({ section, search, ...other }) => {
const [ _section ] = useState(section || 'general');
const { marginBottom20 } = getModule('marginBottom20', 'marginBottom40');
const [ searchSettings, setSearchSettings ] = Settings.useSetting('searchSettings', 'all');
const [ settingsSidebarCollapsed, setSettingsSidebarCollapsed ] = Settings.useSetting('settingsSidebarCollapsed', false);
const [ query, setQuery ] = useState(search || '');
/**
* Handles updating the current search filter.
* @param {string} query Search query
* @returns {void}
*/
const handleQueryChange = query => {
return setQuery(typeof query === 'string' ? query : '');
};
return (
<>
<TextInput
defaultValue={getSetting('commandPrefix', '.')}
onChange={p => updateSetting('commandPrefix', !p ? '.' : p.replace(/\s+(?=\S)|(?<=\s)\s+/g, '').toLowerCase())}
onBlur={({ target }) => target.value = getSetting('commandPrefix', '.')}
error={getSetting('commandPrefix', '.') === '/' ? 'Prefix should not be set to `/` as it is already in use by Discord and may disable Vizality autocompletions.' : ''}
>
{Messages.VIZALITY_COMMAND_PREFIX}
</TextInput>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_NO_CLYDE_DESC.format({ discordiaUrl: 'https://discordia.me/clyde', apiUrl: `${window.location.origin}/vizality/docs` })}
value={getSetting('replaceClyde', true)}
onChange={() => {
try {
toggleSetting('replaceClyde', true);
} catch (err) {
console.log(err);
}
}}
>
<Icon
className='vz-settings-eradicate-clyde-icon-wrapper'
iconClassName='vz-settings-eradicate-clyde-icon'
name='Robot'
size='20px'
<div className='vz-dashboard-settings-search-outer-wrapper'>
<FilterInput
value={query}
onChange={handleQueryChange}
className='vz-dashboard-settings-search-wrapper'
size={FilterInput.Sizes.LARGE}
/>
{Messages.VIZALITY_SETTINGS_NO_CLYDE}
</SwitchItem>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING_DESC.format()}
value={getSetting('smoothScrolling', true)}
onChange={() => {
toggleSetting('smoothScrolling', true);
vizality.api.actions.invokeAction('CONFIRM_RESTART');
}}
>
{Messages.VIZALITY_SETTINGS_SMOOTH_SCROLLING}
</SwitchItem>
<Category
name={Messages.ADVANCED_SETTINGS}
description={Messages.VIZALITY_SETTINGS_ADVANCED_DESC}
opened={getSetting('advancedSettings', false)}
onChange={() => toggleSetting('advancedSettings')}
>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS_DESC.format()}
value={getSetting('reactDeveloperTools', false)}
onChange={() => {
toggleSetting('reactDeveloperTools', false);
vizality.api.actions.invokeAction('CONFIRM_RESTART');
}}
>
{Messages.VIZALITY_SETTINGS_REACT_DEVELOPER_TOOLS}
</SwitchItem>
<SwitchItem
note='Enables live reload for folder/file changes for plugins.'
value={getSetting('hotReload', false)}
onChange={async () => {
toggleSetting('hotReload', false);
await vizality.manager.plugins.remountAll();
}}
>
Enable Hot Reload
</SwitchItem>
{/* <SwitchItem
note={Messages.VIZALITY_SETTINGS_DEBUG_LOGS_DESC}
value={getSetting('debugLogs', false)}
onChange={() => {
toggleSetting('debugLogs');
confirmRestart();
}}
>
{Messages.VIZALITY_SETTINGS_DEBUG_LOGS}
</SwitchItem> */}
{/* <SwitchItem
note={'Vizality\'s Software Development Kit (SDK) is a toolkit created to help make plugin developers\'s and theme developers\' lives easier. Once enabled, you can access it through an icon at the top right hand corner of the channel headerbar.'}
value={getSetting('sdkEnabled', false)}
onChange={() => toggleSetting('sdkEnabled')}
>
Enable Software Development Kit
</SwitchItem>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_OVERLAY_DESC}
value={getSetting('openOverlayDevTools', false)}
onChange={() => toggleSetting('openOverlayDevTools')}
>
{Messages.VIZALITY_SETTINGS_OVERLAY}
</SwitchItem> */}
<SwitchItem
note={Messages.VIZALITY_SETTINGS_KEEP_TOKEN_DESC}
value={getSetting('hideToken', true)}
onChange={() => toggleSetting('hideToken', true)}
>
{Messages.VIZALITY_SETTINGS_KEEP_TOKEN}
</SwitchItem>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_TRANSPARENT_DESC.format()}
value={getSetting('transparentWindow', false)}
onChange={() => {
toggleSetting('transparentWindow');
vizality.api.actions.invokeAction('CONFIRM_RESTART');
}}
>
{Messages.VIZALITY_SETTINGS_TRANSPARENT}
</SwitchItem>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM_DESC.format()}
value={getSetting('experimentalWebPlatform', false)}
onChange={() => {
toggleSetting('experimentalWebPlatform');
vizality.api.actions.invokeAction('CONFIRM_RESTART');
}}
>
{Messages.VIZALITY_SETTINGS_EXP_WEB_PLATFORM}
</SwitchItem>
<SwitchItem
note={Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS_DESC.format()}
value={getSetting('experiments', false)}
onChange={() => {
toggleSetting('experiments');
// Update modules
const experimentsModule = getModule(r => r.isDeveloper !== void 0);
experimentsModule._changeCallbacks.forEach(cb => cb());
}}
{/* <SelectInput
value={searchSettings}
options={[
{
value: 'all',
label: 'All'
},
{
value: 'category',
label: 'Category'
}
]}
onChange={evt => setSearchSettings(evt?.value)}
/> */}
</div>
<Flex className='vz-dashboard-settings-content-wrapper'>
<Flex direction={Flex.Direction.VERTICAL} className='vz-dashboard-settings-content-wrapper-inner'>
<Flex className='vz-dashboard-settings-content' vz-section={_section}>
<div className='vz-dashboard-settings-content-inner'>
<Body props={other} query={query} section={_section} />
</div>
</Flex>
</Flex>
<ContentSidebar
header='Categories'
collapsed={settingsSidebarCollapsed}
onToggle={() => setSettingsSidebarCollapsed(!settingsSidebarCollapsed)}
>
{Messages.VIZALITY_SETTINGS_DISCORD_EXPERIMENTS}
</SwitchItem>
</Category>
<ButtonItem
note={Messages.VIZALITY_SETTINGS_CACHE_VIZALITY_DESC}
button={isVizalityCacheCleared ? Messages.VIZALITY_SETTINGS_CACHE_CLEARED : Messages.VIZALITY_SETTINGS_CACHE_VIZALITY}
success={isVizalityCacheCleared}
onClick={() => clearVizalityCache()}
>
{Messages.VIZALITY_SETTINGS_CACHE_VIZALITY}
</ButtonItem>
<ButtonItem
note={Messages.VIZALITY_SETTINGS_CACHE_DISCORD_DESC}
button={isDiscordCacheCleared ? Messages.VIZALITY_SETTINGS_CACHE_CLEARED : Messages.VIZALITY_SETTINGS_CACHE_DISCORD}
success={isDiscordCacheCleared}
onClick={() => clearDiscordCache()}
>
{Messages.VIZALITY_SETTINGS_CACHE_DISCORD}
</ButtonItem>
<ContentSidebar.NavItem
icon='Science'
route='/vizality/settings/general'
selected={_section === 'general'}
tooltip={settingsSidebarCollapsed && 'General'}
tooltipPosition='left'
>
General
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Person'
route='/vizality/settings/account'
selected={_section === 'account'}
tooltip={settingsSidebarCollapsed && 'Account'}
tooltipPosition='left'
disabled
>
Account
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Palette'
route='/vizality/settings/appearance'
selected={_section === 'appearance'}
tooltip={settingsSidebarCollapsed && 'Appearance'}
tooltipPosition='left'
disabled
>
Appearance<TextBadge text='SOON™' />
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Bell'
route='/vizality/settings/notifications'
selected={_section === 'notifications'}
tooltip={settingsSidebarCollapsed && 'Notifications'}
tooltipPosition='left'
>
Notifications
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Keyboard'
route='/vizality/settings/keybinds'
selected={_section === 'keybinds'}
tooltip={settingsSidebarCollapsed && 'Keybinds'}
tooltipPosition='left'
disabled
>
Keybinds
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='ApplicationCommand'
route='/vizality/settings/commands'
selected={_section === 'commands'}
tooltip={settingsSidebarCollapsed && 'Commands'}
tooltipPosition='left'
>
Commands
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Robot'
route='/vizality/settings/developer'
selected={_section === 'developer'}
tooltip={settingsSidebarCollapsed && 'Developer'}
tooltipPosition='left'
>
Developer
</ContentSidebar.NavItem>
<ContentSidebar.NavItem
icon='Wrench'
route='/vizality/settings/advanced'
selected={_section === 'advanced'}
tooltip={settingsSidebarCollapsed && 'Advanced'}
tooltipPosition='left'
>
Advanced
</ContentSidebar.NavItem>
</ContentSidebar>
</Flex>
</>
);
});

@ -1,25 +1,71 @@
import React from 'react';
import { open as openModal, close as closeModal } from '@vizality/modal';
import { getModuleByDisplayName, getModule } 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 } from '@vizality/components';
import { joinClassNames } from '@vizality/util/dom';
import { patch, unpatch } from '@vizality/patcher';
import { Confirm } from '@vizality/components';
import { Builtin } from '@vizality/entities';
import { Messages } from '@vizality/i18n';
import React from 'react';
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 => (
<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: 'General',
description: categories.general.description,
icon: categories.general.icon,
render: props => <SettingsPage {...props} builtin={this} />
header: 'Settings',
icon: content.icon,
render: props => <SettingsPage {...props} builtin={this} section='general' />
});
vizality.api.actions.registerAction('CONFIRM_RESTART', () => this.confirmRestart());
@ -28,14 +74,17 @@ export default class Settings extends Builtin {
this.patchExperiments();
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-items');
unpatch('vz-settings-context-menu');
unpatch('vz-settings-context-menu-addon-items');
unpatch('vz-settings-context-menu-addon-checkbox-items');
}
confirmRestart () {
@ -117,18 +166,103 @@ export default class Settings extends Builtin {
});
}
patchSettingsContextMenuAddonItem () {
patchSettingsContextMenuAddonCheckboxItem () {
const MenuCheckboxItem = getModule(m => m.default?.displayName === 'MenuCheckboxItem');
patch('vz-settings-context-menu-addon-items', MenuCheckboxItem, 'default', ([ props ], res) => {
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('user-settings-cog-vizality--plugins--') &&
res.props?.id?.indexOf('user-settings-cog-vizality--themes--') &&
res.props?.id?.indexOf('vizality-dashboard-plugins') &&
res.props?.id?.indexOf('vizality-dashboard-themes')) ||
res.props['vz-addon-icon']
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;

@ -1,20 +1,138 @@
.vz-settings {
&-eradicate-clyde-icon-wrapper {
vertical-align: sub;
margin-right: 4px;
@use '@vizality' as vz;
/**
* Vizality (Dashboard) Settings Context Menu
*/
.vz-settings-context-menu-search {
[vz-addon-icon] .label-22pbtT {
display: flex;
align-items: center;
&::before {
@include vz.size(20px);
content: '';
background: var(--vz-addon-icon) no-repeat center / contain;
display: flex;
border-radius: 4px;
margin-right: 12px;
}
}
.groupLabel-2t5iuZ {
justify-content: flex-start;
background: none !important;
padding: 0;
> div {
pointer-events: all;
width: 100%;
padding: 3px 6px;
}
+ .separator-2I32lJ {
margin: 5px 0 10px;
}
}
}
[vz-addon-icon] .label-22pbtT {
display: flex;
align-items: center;
&::before {
content: '';
width: 20px;
height: 20px;
background: var(--vz-addon-icon) no-repeat center / contain;
display: flex;
border-radius: 4px;
margin-right: 12px;
.menu-3sdvDG {
.textBadge-1iylP6 {
display: inline;
margin-left: 6px;
font-size: 10px;
}
}
.vz-dashboard-content-sidebar {
$base: &;
[vz-builtin='settings'] & {
height: fit-content;
width: 240px;
background: var(--background-secondary-alt);
border-radius: 8px;
padding: 16px;
z-index: 1;
margin: 40px 0 0 40px;
top: 90px;
&[vz-collapsed] {
flex-direction: column;
width: 60px !important;
min-width: 60px;
max-width: 60px;
height: fit-content !important;
padding: 16px;
#{$base}-inner {
width: 60px;
display: flex;
}
#{$base}-expander {
@include vz.size(42px);
}
/**
* Expander wrapper
*/
> div:first-of-type {
margin-top: -10px;
margin-bottom: 10px;
}
#{$base}-items {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#{$base}-header-wrapper {
display: none;
}
#{$base}-item-wrapper {
margin: 2px 0;
&:last-of-type {
margin: 2px 0 0;
}
.vz-dashboard-sidebar-item-inner {
padding: 2px;
}
}
.vz-dashboard-sidebar-item-icon-wrapper {
margin: 0;
}
.content-3QAtGj {
display: none;
}
}
}
}
.vz-dashboard-settings {
&-content-inner {
flex: 1;
margin-top: 40px;
}
&-search-outer-wrapper {
position: sticky;
padding-top: 20px;
background: var(--background-primary);
top: 0;
z-index: 1;
margin-top: -20px;
padding-bottom: 20px;
+ .dividerDefault-3rvLe- {
margin-top: 0;
}
}
&-search-wrapper {
flex: 0 auto;
background: var(--background-secondary-alt);
border-radius: 8px;
border: 1px solid transparent;
&:focus-within {
border-color: var(--brand-experiment);
}
}
}
.vz-filter-input {
width: 100%;
position: sticky;
top: 0;
padding: 20px 0;
box-sizing: border-box;
background: var(--background-primary);
z-index: 1;
}

@ -1,33 +1,23 @@
import { SwitchItem, TextInput, Category, FormTitle } from '@vizality/components/settings';
import { Confirm, Clickable, Button, FormNotice } from '@vizality/components';
import { Confirm, Button, FormNotice } from '@vizality/components';
import { open as openModal, close as closeModal } from '@vizality/modal';
import { Repositories, Directories } from '@vizality/constants';
import React, { useEffect, memo, useState } from 'react';
import { Messages, chosenLocale } from '@vizality/i18n';
import { Repositories } from '@vizality/constants';
import React, { useEffect, memo } from 'react';
import { Messages } from '@vizality/i18n';
import { joinClassNames } from '@vizality/util/dom';
import { getModule } from '@vizality/webpack';
import { readdirSync, existsSync } from 'fs';
import { clipboard } from 'electron';
import moment from 'moment';
import Update from './Update';
import Icons from './Icons';
export default memo(({ getSetting, toggleSetting, updateSetting }) => {
const [ opened, setOpened ] = useState(false);
const [ copyText, setCopyText ] = useState(Messages.COPY);
const [ debugInfoOpened, setDebugInfoOpened ] = useState();
const [ pathsRevealed, setPathsRevealed ] = useState();
const [ pluginsRevealed, setPluginsRevealed ] = useState();
useEffect(() => {
const updateToasts = [
'vizality-updater-update-complete',
'vizality-updater-update-available'
];
for (const toast of updateToasts) {
vizality.api.notifications.closeToast(toast);
}
updateToasts.forEach(t => vizality.api.notifications.closeToast(t));
});
// eslint-disable-next-line consistent-this
@ -198,134 +188,6 @@ export default memo(({ getSetting, toggleSetting, updateSetting }) => {
);
};
const handleDebugInfoCopy = (plugins) => {
const extract = document.querySelector('.vz-updater-debug-info > code')
.innerText.replace(/([A-Z/ ]+) (?=\s(?!C:\\).*?:)/g, '\n[$1]').replace(/(.*?):\s(.*.+)/g, '$1="$2"').replace(/[ -](\w*(?=.*=))/g, '$1');
setCopyText(Messages.COPIED);
clipboard.writeText(
`\`\`\`ini
# Debugging Information | Result created: ${moment().calendar()}
${extract.substring(0, extract.indexOf('\nPlugins', extract.indexOf('\nPlugins') + 1))}
Plugins="${plugins.join(', ')}"
\`\`\``.replace(/ {6}|n\/a/g, '').replace(/(?![0-9]{1,3}) \/ (?=[0-9]{1,3})/g, '/')
);
setTimeout(() => setCopyText(Messages.COPY), 2500);
};
// --- DEBUG STUFF (Intentionally left english-only)
const renderDebugInfo = () => {
const { getRegisteredExperiments, getExperimentOverrides } = getModule('initialize', 'getExperimentOverrides');
const superProperties = getModule('getSuperPropertiesBase64').getSuperProperties();
const plugins = vizality.manager.plugins.getEnabledKeys();
const experimentOverrides = Object.keys(getExperimentOverrides()).length;
const availableExperiments = Object.keys(getRegisteredExperiments()).length;
const discordPath = process.resourcesPath.slice(0, -10);
const maskPath = (path) => {
path = path.replace(/(?:\/home\/|C:\\Users\\|\/Users\/)([ \w.-]+).*/i, (path, username) => {
const usernameIndex = path.indexOf(username);
return [ path.slice(0, usernameIndex), username.charAt(0) + username.slice(1).replace(/[a-zA-Z]/g, '*'),
path.slice(usernameIndex + username.length) ].join('');
});
return path;
};
const cachedFiles = (existsSync(Directories.CACHE) && readdirSync(Directories.CACHE)
.map(d => readdirSync(`${Directories.CACHE}/${d}`))
.flat().length) || 'n/a';
const createPathReveal = (title, path) =>
<div className='full-column'>
{title}:&#10;<a
onMouseEnter={() => setPathsRevealed(true)}
onMouseLeave={() => setPathsRevealed(false)}
onClick={() => window.DiscordNative.fileManager.showItemInFolder(path)}
>{pathsRevealed ? path : maskPath(path)}</a>
</div>;
return <FormNotice
type={FormNotice.Types.PRIMARY}
body={<div className={ joinClassNames('vz-updater-debug-info', { copied: copyText === Messages.COPIED })}>
<code>
<b>System / Discord</b>
<div className='row'>
<div className='column'>Locale:&#10;{chosenLocale}</div>
<div className='column'>OS:&#10;{(window.platform.os).toString()}</div>
<div className='column'>Architecture:&#10;{superProperties.os_arch}</div>
{process.platform === 'linux' && (
<div className='column'>Distro:&#10;{superProperties.distro || 'n/a'}</div>
)}
<div className='column'>Release Channel:&#10;{superProperties.release_channel}</div>
<div className='column'>App Version:&#10;{superProperties.client_version}</div>
<div className='column'>Build Number:&#10;{superProperties.client_build_number}</div>
<div className='column'>Build ID:&#10;{window.GLOBAL_ENV.SENTRY_TAGS.buildId}</div>
<div className='column'>Experiments:&#10;{experimentOverrides} / {availableExperiments}</div>
</div>
<b>Process Versions</b>
<div className='row'>
<div className='column'>React:&#10;{React.version}</div>
{[ 'electron', 'chrome', 'node' ].map(proc =>
<div className='column'>{proc.charAt(0).toUpperCase() + proc.slice(1)}:&#10;{process.versions[proc]}</div>
)}
</div>
<b>Vizality</b>
<div className='row'>
<div className='column'>Commands:&#10;{vizality.api.commands.getAllCommands().length}</div>
<div className='column'>Settings:&#10;{Object.keys(vizality.api.settings?.store?.getAllSettings()).length}</div>
<div className='column'>Plugins:&#10;{vizality.manager.plugins.getEnabledKeys().length} / {vizality.manager.plugins.count}
</div>
<div className='column'>Themes:&#10;{vizality.manager.themes.getEnabledKeys().length} / {vizality.manager.themes.count}
</div>
<div className='column'>Cached Files:&#10;{cachedFiles}</div>
<div className='column'>APIs:&#10;{vizality.manager.apis._apis.length}</div>
</div>
<b>Git</b>
<div className='row'>
<div className='column'>Upstream:&#10;{vizality.git.upstream.replace(Repositories.VIZALITY, 'Official')}</div>
<div className='column'>Revision:&#10;
<a
href={`https://github.com/${vizality.git.upstream}/commit/${vizality.git.revision}`}
target='_blank'
>
[{vizality.git.revision.substring(0, 7)}]
</a>
</div>
<div className='column'>Branch:&#10;{vizality.git.branch}</div>
<div className='column'>{`Latest:\n${!getSetting('updates', []).find(update => update.updateId === 'vizality')}`}</div>
</div>
<b>Listings</b>
<div className='row'>
{createPathReveal('Vizality Path', vizality.dir)}
{createPathReveal('Discord Path', discordPath)}
<div className='full-column'>Experiments:&#10;{experimentOverrides ? Object.keys(getExperimentOverrides()).join(', ') : 'n/a'}</div>
<div className='full-column'>
Plugins:&#10;
{(plugins.length > 6 ? `${(pluginsRevealed ? plugins : plugins.slice(0, 6)).join(', ')}` : plugins.join(', ')) || 'n/a'}&nbsp;
{plugins.length > 6 &&
<Clickable tag='a' onClick={() => setPluginsRevealed(!pluginsRevealed)}>
{pluginsRevealed ? 'Show less' : 'Show more'}
</Clickable>}
</div>
</div>
</code>
<Button
size={Button.Sizes.SMALL}
color={copyText === Messages.COPIED ? Button.Colors.GREEN : Button.Colors.BRAND}
onClick={() => handleDebugInfoCopy(plugins)}
>
{copyText}
</Button>
</div>}
/>;
};
return (
<>
{awaitingReload && renderReload()}
@ -434,24 +296,21 @@ export default memo(({ getSetting, toggleSetting, updateSetting }) => {
);
})}
</div>}
{disabledAddons.length > 0 && <Category
name={Messages.VIZALITY_UPDATES_DISABLED_SECTION}
description={Messages.VIZALITY_UPDATES_DISABLED_SECTION_DESC}
opened={opened}
onChange={() => setOpened(!opened)}
>
{disabledAddons.map(update => {
return (
<Update
{...update}
disabled={true}
key={update.updateId}
updating={updating}
onEnableUpdates={() => _this.enableUpdates(update)}
/>
);
})}
</Category>}
{disabledAddons.length > 0 && (
<Category title={Messages.VIZALITY_UPDATES_DISABLED_SECTION} description={Messages.VIZALITY_UPDATES_DISABLED_SECTION_DESC}>
{disabledAddons.map(update => {
return (
<Update
{...update}
disabled={true}
key={update.updateId}
updating={updating}
onEnableUpdates={() => _this.enableUpdates(update)}
/>
);
})}
</Category>
)}
<div className='vz-updater-options'>
<FormTitle className='vz-updater-options-title'>
{Messages.OPTIONS}
@ -490,14 +349,6 @@ export default memo(({ getSetting, toggleSetting, updateSetting }) => {
>
{Messages.VIZALITY_UPDATES_OPTS_CONCURRENCY}
</TextInput>
<Category
name={Messages.VIZALITY_UPDATES_OPTS_DEBUG}
description={Messages.VIZALITY_UPDATES_OPTS_DEBUG_DESC}
opened={debugInfoOpened}
onChange={() => setDebugInfoOpened(!debugInfoOpened)}
>
{renderDebugInfo()}
</Category>
</div>
</>
);

Loading…
Cancel
Save