Adding my experimental plugins and fixed some stuff

pull/2/head
BakedPVP 4 years ago
parent 95994349ea
commit e73b8c8dac

@ -18,7 +18,7 @@
"VIZALITY_CUTIE_TIER_EASTER_DESC": "**Vizality Developers** will visit you wherever you live to **hug you**.",
"VIZALITY_CUTIE_TIER_EASTER_PRICE": "**$1,337,420.69**",
"VIZALITY_CUTIE_TITLE": "Support Vizality's Development",
"VIZALITY_GENERAL_SETTINGS": "General Settings",
"VIZALITY_GENERAL_SETTINGS": "Settings",
"VIZALITY_I18N_CONTRIBUTE": "Psst! Want to help translate Vizality? Go to our [Weblate]({weblateUrl})!",
"VIZALITY_I18N_TRANSLATED_OVERRIDES": "{overrides,number} overrides to Discord's translations",
"VIZALITY_I18N_TRANSLATED_PERCENTAGE": "Vizality: {translated,number}% translated",

@ -1,5 +1,5 @@
// Shorthand for `color` CSS custom properties
// @TODO: Needs improved to work with fallback values
// @todo: Needs improved to work with fallback values
@function color($propertyName, $alpha: null) {
@if $alpha {

@ -9,6 +9,9 @@
.vz-hasNoGameIcon {
background-image: url('https://i.imgur.com/Fa13xn9.png');
}
.vz-hasNoGameIcon * {
display: none;
}
.vz-hasNoUserAvatar {

@ -0,0 +1,28 @@
const { Plugin } = require('vizality/entities');
const { getModule } = require('vizality/webpack');
const electron = require('electron').remote;
const webContents = electron.getCurrentWebContents();
const currentWindow = electron.getCurrentWindow();
module.exports = class ChannelHistory extends Plugin {
startPlugin () {
currentWindow.on('app-command', this.listener);
}
async listener (ev, cmd) {
if (cmd !== 'browser-backward' && cmd !== 'browser-forward') {
return;
}
if (cmd === 'browser-backward' && webContents.canGoBack()) {
(await getModule([ 'history' ])).history.back();
} else if (cmd === 'browser-forward' && webContents.canGoForward()) {
(await getModule([ 'history' ])).history.forward();
}
}
pluginWillUnload () {
currentWindow.off('app-command', this.listener);
}
};

@ -0,0 +1,8 @@
{
"name": "Channel History",
"version": "1.0.0",
"description": "Allows you to navigate forwards and backwards with your side mouse buttons, like in the browser.",
"author": "Vizality",
"license": "GPL-3.0",
"color": "#33FFCC"
}

@ -0,0 +1,72 @@
/* eslint-disable prefer-destructuring */
const { Plugin } = require('vizality/entities');
const { React, getModuleByDisplayName } = require('vizality/webpack');
const { inject, uninject } = require('vizality/injector');
const { Tooltip } = require('vizality/components');
module.exports = class ChannelMembersActivityIcons extends Plugin {
startPlugin () {
this.loadStylesheet('style.scss');
this._injectActivityIcons();
}
async _injectActivityIcons () {
const MemberListItem = await getModuleByDisplayName('MemberListItem');
inject('channelMembersActivityIcons-members', MemberListItem.prototype, 'render', (_, res) => {
if (!res ||
!res.props ||
!res.props.subText ||
!res.props.subText.props ||
!res.props.subText.props.activities.length
) {
return res;
}
for (const activity of res.props.subText.props.activities) {
if (activity.application_id &&
activity.assets &&
activity.assets.small_image
) {
res.props.children =
React.createElement(Tooltip, {
text: activity.name,
position: 'left'
}, React.createElement(
'div', {
className: 'vizality-members-activity-icon',
style: { backgroundImage: `url(https://cdn.discordapp.com/app-assets/${activity.application_id}/${activity.assets.small_image}.png)` }
}
));
res.props.className += ' vz-hasActivityIcon';
return res;
}
if (activity.type && activity.type === 2) {
res.props.children =
React.createElement(Tooltip, {
text: 'Spotify',
position: 'left'
}, React.createElement(
'div', {
className: 'vizality-members-activity-icon',
style: { backgroundImage: 'url(\'/assets/f0655521c19c08c4ea4e508044ec7d8c.png\')' }
}
));
res.props.className += ' vz-hasActivityIcon';
return res;
}
}
return res;
});
}
pluginWillUnload () {
uninject('channelMembersActivityIcons-members');
}
};

@ -0,0 +1,26 @@
.membersWrap-2h-GB4 {
.vizality-members-activity-icon {
height: 24px;
width: 24px;
border-radius: 4px;
margin-left: 5px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
// Remove custom status emoji and rich presence icon
.emoji-2cWBLE,
.icon-15YQ1T {
display: none;
}
.content-3QAtGj {
margin-right: 37px; // 8px (left margin) + 24px (icon size) + 5px (left margin)
}
.vz-hasActivityIcon {
.content-3QAtGj {
margin: 0;
}
}
}

@ -0,0 +1,7 @@
{
"name": "Channel Members Activity Icons",
"version": "0.0.1",
"description": "Adds activity icons to the channel members list for users' currently playing activity.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,131 @@
const { Plugin } = require('vizality/entities');
const { inject, uninject } = require('vizality/injector');
const { React, getModule } = require('vizality/webpack');
const { classNames } = require('vizality/util');
const { Icon } = require('vizality/components');
module.exports = class UtilityClasses extends Plugin {
startPlugin () {
this.loadStylesheet('style.scss');
this._injectContextMenuItems();
this._injectContextMenuCheckboxItems();
this._injectContextMenuControlItems();
}
async _injectContextMenuItems () {
const MenuItem = await getModule(m => m.default && m.default.displayName === 'MenuItem');
inject('vz-contextMenuIcons', MenuItem, 'default', (originalArgs, returnValue) => {
if (!returnValue.props || !returnValue.props.id) return returnValue;
const { id } = returnValue.props;
returnValue.props.className = classNames(returnValue.props.className, 'vz-hasIcon');
const type = [
id === 'textarea-context-languages' && 'atom',
id === 'textarea-context-copy' && 'atom',
id === 'textarea-context-cut' && 'atom',
id === 'textarea-context-paste' && 'atom',
id === 'user-context-user-profile' && 'atom',
id === 'user-context-mention' && 'atom',
id === 'user-context-message-user' && 'atom',
id === 'user-context-call' && 'atom',
id === 'user-context-note' && 'atom',
id === 'user-context-invite-to-server' && 'atom',
id === 'user-context-add-friend' && 'atom',
id === 'user-context-remove-friend' && 'atom',
id === 'user-context-block' && 'atom',
id === 'user-context-roles' && 'atom',
id === 'user-context-devmode-copy-id' && 'atom',
id === 'message-add-reaction' && 'atom',
id === 'message-edit' && 'atom',
id === 'message-pin' && 'atom',
id === 'message-quote' && 'atom',
id === 'message-mark-unread' && 'atom',
id === 'message-copy-link' && 'atom',
id === 'message-copy-native-link' && 'atom',
id === 'message-open-native-link' && 'atom',
id === 'message-delete' && 'atom',
id === 'message-devmode-copy-id' && 'atom'
].filter(Boolean).join(' ');
if (!type) {
console.log('Context item not specified yet: ', id);
return returnValue;
}
const icon = React.createElement(Icon, { type,
className: 'vizality-contextMenuIcon' });
returnValue.props.children.unshift(icon);
return returnValue;
});
}
async _injectContextMenuCheckboxItems () {
const MenuCheckboxItem = await getModule(m => m.default && m.default.displayName === 'MenuCheckboxItem');
inject('vz-contextMenuCheckboxIcons', MenuCheckboxItem, 'default', (originalArgs, returnValue) => {
if (!returnValue.props || !returnValue.props.id) return returnValue;
const { id } = returnValue.props;
returnValue.props.className = classNames(returnValue.props.className, 'vz-hasIcon');
const type = [
id === 'textarea-context-spellcheck' && 'atom',
id === 'user-context-disable-video' && 'atom',
id === 'user-context-mute' && 'atom'
].filter(Boolean).join(' ');
if (!type) {
console.log('Context checkbox item not specified yet: ', id);
return returnValue;
}
const icon = React.createElement(Icon, { type,
className: 'vizality-contextMenuIcon' });
returnValue.props.children.unshift(icon);
return returnValue;
});
}
async _injectContextMenuControlItems () {
const MenuControlItem = await getModule(m => m.default && m.default.displayName === 'MenuControlItem');
inject('vz-contextMenuControlIcons', MenuControlItem, 'default', (originalArgs, returnValue) => {
if (!returnValue.props || !returnValue.props.id || !returnValue.props.children || !returnValue.props.children[0].props) return returnValue;
const { id } = returnValue.props;
returnValue.props.className = classNames(returnValue.props.className, 'vz-hasIcon');
const type = [
id === 'user-context-user-volume' && 'atom'
].filter(Boolean).join(' ');
if (!type) {
console.log('Context checkbox item not specified yet: ', id);
return returnValue;
}
const icon = React.createElement(Icon, { type,
className: 'vizality-contextMenuIcon' });
returnValue.props.children.unshift(icon);
return returnValue;
});
}
pluginWillUnload () {
uninject('vz-contextMenuIcons');
uninject('vz-contextMenuCheckboxIcons');
uninject('vz-contextMenuControlIcons');
}
};

@ -0,0 +1,36 @@
@use '../../library' as *;
.menu-3sdvDG {
.vizality-icon-container {
@include size(18px);
display: inline-flex;
margin-right: 12px;
}
.vizality-icon {
@include size(100%);
}
}
// .menu-3sdvDG {
// .label-22pbtT {
// order: 1;
// }
// .iconContainer-2-XQPY {
// order: 3;
// }
// .vizality-icon-container {
// order: 2;
// }
// .imageContainer-2fGqYU {
// order: 3;
// }
// }
.vizality-contextMenuIcon-guildIcon {
@include size(20px);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
display: inline-flex;
border-radius: 50%;
}

@ -0,0 +1,7 @@
{
"name": "Context Menu Icons",
"version": "0.0.1",
"description": "Adds utility classes and icons to the left of context menu items.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,4 @@
# Copy Raw Message
Lets you copy messages with its formatting still intact, you should enable
Developer Mode in Appearance settings

@ -0,0 +1,34 @@
const { Plugin } = require('vizality/entities');
const { inject, uninject } = require('vizality/injector');
const { React, getModule } = require('vizality/webpack');
const { Menu } = require('vizality/components');
module.exports = class CopyRawMessage extends Plugin {
startPlugin () {
this._patchContextMenu();
}
pluginWillUnload () {
uninject('copy-raw-message');
}
async _patchContextMenu () {
const MessageContextMenu = await getModule(m => m.default && m.default.displayName === 'MessageContextMenu');
inject('copy-raw-message', MessageContextMenu, 'default', ([ props ], returnValue) => {
const { message } = props;
if (!message || !message.content) return returnValue;
returnValue.props.children.push(
React.createElement(Menu.MenuItem, {
label: 'Copy Raw Message',
id: 'copy-raw-message',
action: async () => DiscordNative.clipboard.copy(message.content)
})
);
return returnValue;
});
}
};

@ -0,0 +1,7 @@
{
"name": "Copy Raw Message",
"description": "Lets you copy messages with its formatting still intact",
"version": "1.1.0",
"author": ".intrnl#6380",
"license": "GPL-3.0"
}

@ -0,0 +1,25 @@
/* eslint-disable prefer-destructuring */
const { Plugin } = require('vizality/entities');
const { React, getModuleByDisplayName, getModule, i18n } = require('vizality/webpack');
const { inject, uninject } = require('vizality/injector');
const { findInReactTree, forceUpdateElement } = require('vizality/util');
module.exports = class CustomBanners extends Plugin {
startPlugin () {
this.loadStylesheet('style.scss');
this._patchPrivateChannelEmptyMessage();
}
async _patchPrivateChannelEmptyMessage () {
const PrivateChannelEmptyMessage = await getModuleByDisplayName('PrivateChannelEmptyMessage');
inject('pc-impChannelTitlebar-privateChannelsEmptyMessage', PrivateChannelEmptyMessage.prototype, 'render', (_, res) => {
console.log(res);
return res;
});
return async () => uninject('pc-impChannelTitlebar-privateChannelsEmptyMessage');
}
};

@ -0,0 +1,27 @@
@use '../../library' as *;
.container-Vw8GSP {
padding: 30px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-flow: column;
margin-bottom: 50px;
position: relative;
* {
z-index: 1;
}
.wrapper-3t9DeA {
border: 3px solid var(--header-primary);
}
&::after {
@include size(100%);
content: '';
position: absolute;
top: 0;
left: 0;
background: linear-gradient(to top, var(--background-primary), transparent);
}
}

@ -0,0 +1,7 @@
{
"name": "Custom Banners",
"version": "0.0.1",
"description": "Adds Vizality's custom user profile banners and custom server banners to various parts of Discord.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,209 @@
/* eslint-disable prefer-destructuring */
const { Plugin } = require('vizality/entities');
const { React, getModuleByDisplayName, getModule, i18n } = require('vizality/webpack');
const { inject, uninject } = require('vizality/injector');
const { findInReactTree } = require('vizality/util');
module.exports = class ChannelTitlebar extends Plugin {
constructor () {
super();
this.guildHeader = null;
this.activityPre = null;
this.activity = null;
this.activitySong = null;
this.activityArtist = null;
}
startPlugin () {
this.loadStylesheet('style.scss');
this._getGuildChannelHeader();
this.improvedChannelHeader();
}
async _getGuildChannelHeader () {
const GuildHeader = await getModuleByDisplayName('GuildHeader');
inject('pc-impChannelTitlebar-guildChannelHeader', GuildHeader.prototype, 'renderHeader', (_, res) => {
this.guildHeader = res;
return res;
});
return async () => uninject('pc-impChannelTitlebar-guildChannelHeader');
}
async improvedChannelHeader () {
const ChannelHeader = await getModuleByDisplayName('HeaderBarContainer');
inject('pc-impChannelTitlebar-channelHeader', ChannelHeader.prototype, 'render', (_, res) => {
const found = findInReactTree(res, n => n.channel);
if (!found) {
return res;
}
let icon,
iconId,
iconType,
noIconURL,
activity,
userActivity;
// @TODO: Fix this to check for status updates every 7 seconds or something
// Guild channel
if (found.channel.type === 0) {
iconId = found.guild.id;
icon = found.guild.icon;
iconType = 'icons';
noIconURL = 'https://i.imgur.com/Fa13xn9.png';
// User DM channel
} else if (found.channel.type === 1) {
iconId = found.channel.rawRecipients[0].id;
icon = found.channel.rawRecipients[0].avatar;
iconType = 'avatars';
noIconURL = document.querySelector('.channel-2QD9_O.selected-aXhQR6 .avatar-VxgULZ') ? document.querySelector('.channel-2QD9_O.selected-aXhQR6 .avatar-VxgULZ').src : '';
activity = (getModule([ 'getPrimaryActivity' ], false)).getPrimaryActivity(iconId);
if (activity) {
if (activity.type === 0) {
this.activityPre = 'Playing';
this.activity = activity.name;
} else if (activity.type === 1) {
this.activityPre = 'Streaming';
this.activity = activity.details;
} else if (activity.type === 2) {
this.activityPre = i18n._proxyContext.messages.ACTIVITY_FEED_NOW_PLAYING_SPOTIFY.split(' ');
this.activityPre.pop();
this.activityPre = this.activityPre.join(' ');
this.activitySong = activity.details;
this.activityArtist = activity.state;
} else if (activity.type === 3) {
this.activityPre = 'Watching';
this.activity = activity.name;
} else if (activity.type === 4 && activity.state) {
this.activityPre = null;
this.activity = activity.state;
} else {
this.activityPre = null;
this.activitySong = null;
this.activityArtist = null;
this.activity = null;
}
}
// Group DM channel
} else if (found.channel.type === 3) {
iconId = found.channel.id;
icon = found.channel.icon;
iconType = 'channel-icons';
noIconURL = document.querySelector('.channel-2QD9_O.selected-aXhQR6 .avatar-VxgULZ') ? document.querySelector('.channel-2QD9_O.selected-aXhQR6 .avatar-VxgULZ').src : '';
}
const iconURL = icon
? `url('https://cdn.discordapp.com/${iconType}/${iconId}/${icon}.png')`
: `url(${noIconURL})`;
if (found.channel.type === 1 && activity) {
if (activity.type === 2) {
userActivity =
React.createElement(
'div', {
className: 'vz-channel-sub-navigation-user-activity'
},
/*
* React.createElement(
* 'span', {
* className: 'vz-channel-sub-navigation-user-activity-icon',
* style: { backgroundImage: 'url(\'/assets/f0655521c19c08c4ea4e508044ec7d8c.png\')' }
* }
* ),
*/
React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-pretext',
children: this.activityPre
}
),
React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-song',
children: this.activitySong
}
),
React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-by',
children: 'by'
}
),
React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-artist',
children: this.activityArtist
}
)
);
} else {
userActivity =
React.createElement(
'div', {
className: 'vz-channel-sub-navigation-user-activity'
},
/*
* activity.type === 0 && activity.application_id && activity.assets && activity.assets.small_image
* ? React.createElement(
* 'span', {
* className: 'vz-channel-sub-navigation-user-activity-icon',
* style: { backgroundImage: `url('https://cdn.discordapp.com/app-assets/${activity.application_id}/${activity.assets.small_image}.png')` }
* }
* )
* : '',
*/
this.activityPre
? React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-pretext',
children: this.activityPre
}
)
: '',
React.createElement(
'span', {
className: 'vz-channel-sub-navigation-user-activity-text',
children: this.activity
}
)
);
}
}
const children = [
React.createElement(
'div', {
className: 'vz-channel-sub-navigation-icon',
style: {
backgroundImage: iconURL
}
}
),
React.createElement(
'div', {
className: 'vz-channel-sub-navigation'
},
found.channel.type === 0 ? this.guildHeader : '',
res.props.children,
userActivity ? userActivity : ''
)
];
res.props.children = children;
return res;
});
return async () => uninject('pc-impChannelTitlebar-channelHeader');
}
};

@ -0,0 +1,127 @@
.title-3qD0b- {
padding: 0 10px;
height: 64px;
.vz-channel-sub-navigation {
position: relative;
flex: 1 1 auto;
display: flex;
min-width: 0;
overflow: hidden;
align-items: center;
flex-flow: row wrap;
.iconWrapper-2OrFZ1,
.icon-22AiRD,
.divider-3FBTu8,
.akaBadge-1M-1Gw,
.nicknames-1XK4Zt {
display: none;
}
.topic-TCb_qw {
margin: 0;
flex: 1 0 100%;
}
.status-1XNdyw {
margin: 0;
}
.title-29uC1r {
flex: none;
overflow: hidden;
text-overflow: ellipsis;
display: block;
@at-root [vz-route='guild'] & {
flex: 1 0;
opacity: .9;
font-size: 14px;
&::before {
content: '#';
margin-right: 1px;
}
}
@at-root [vz-route='dm'] & {
&::before {
content: none;
}
}
}
.spacer-3kEb8l {
display: none;
}
.header-2o-2hj {
height: auto;
padding: 0;
margin-right: 8px;
box-shadow: none;
}
.guildIconContainer-E1JUVt {
cursor: pointer;
}
.name-3YKhmS {
font-size: 16px;
line-height: 22px;
}
.button-1w5pas {
display: none;
}
.vz-channel-sub-navigation-user-activity {
flex: 1 0 100%;
font-size: 14px;
color: var(--interactive-normal);
line-height: 1.4;
display: flex;
align-items: center;
white-space: nowrap;
> span {
+ span {
margin-left: 4px;
}
}
}
.vz-channel-sub-navigation-user-activity-icon {
height: 16px;
width: 16px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
border-radius: 50%;
flex: 0 0 auto;
margin-right: 2px;
display: none;
}
.vz-channel-sub-navigation-user-activity-song,
.vz-channel-sub-navigation-user-activity-artist {
color: var(--interactive-hover);
font-weight: 700;
}
.vz-channel-sub-navigation-user-activity-pretext {
+ .vz-channel-sub-navigation-user-activity-text {
font-weight: 700;
}
}
}
.vz-channel-sub-navigation-icon {
height: 46px;
width: 46px;
margin-right: 12px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
border-radius: 50%;
flex: 0 0 auto;
}
}
.container-2Rl01u {
.header-2o-2hj {
display: none;
}
&.hasBanner-14PPlG {
~ .scrollerWrap-2lJEkd {
> .scroller-2FKFPG {
> div {
padding-top: 48px
}
}
}
}
}

@ -0,0 +1,7 @@
{
"name": "Improved Channel Titlebar",
"version": "0.0.1",
"description": "Addsa a sa",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,69 @@
const { React, getModuleByDisplayName, getModule } = require('vizality/webpack');
const { forceUpdateElement } = require('vizality/util');
const navigate = require('vizality/navigate');
const { AsyncComponent, Icon, Button, Tooltip, Clickable } = require('vizality/components');
const AccountPanel = AsyncComponent.from(getModuleByDisplayName('AccountConnected'));
module.exports = class MainNav extends React.Component {
render () {
return (
<nav className='vizality-main-nav'>
<div className='vizality-main-nav__section-left'>
<div className='vizality-main-nav__logo'></div>
<div className='vizality-main-nav__link' onClick={() => navigate.to('dm')}>
<Icon wrapperClassName='vizality-main-nav__link-icon-wrapper' type='mail'></Icon>
<p className='vizality-main-nav__link-text'>Messages</p>
</div>
<div className='vizality-main-nav__link' onClick={() => navigate.to('discover')}>
<Icon wrapperClassName='vizality-main-nav__link-icon-wrapper' type='discover'></Icon>
<p className='vizality-main-nav__link-text'>Discover</p>
</div>
<div className='vizality-main-nav__link' onClick={() => navigate.to('friends')}>
<Icon wrapperClassName='vizality-main-nav__link-icon-wrapper' type='users'></Icon>
<p className='vizality-main-nav__link-text'>Friends</p>
</div>
<div className='vizality-main-nav__link' onClick={() => navigate.to('library')}>
<Icon wrapperClassName='vizality-main-nav__link-icon-wrapper' type='backpack'></Icon>
<p className='vizality-main-nav__link-text'>Library</p>
</div>
</div>
<div className='vizality-main-nav__section-middle'>
<div className='vizality-main-nav__search'>
<div className='vizality-main-nav__search-inner'>
<input className='vizality-main-nav__search-input' type='text' placeholder='Search' />
<Icon wrapperClassName='vizality-main-nav__search-icon-wrapper' type='search'></Icon>
</div>
</div>
</div>
<div className='vizality-main-nav__section-right'>
<div className='vizality-main-nav__inbox' onClick={async () => (await getModule([ 'TOGGLE_INBOX' ])).TOGGLE_INBOX.action()}>
<Icon wrapperClassName='vizality-main-nav__inbox-icon-wrapper' type='notification-bell'></Icon>
</div>
<div className='vizality-main-nav__account-panel'>
<div className='vizality-main-nav__account-panel-inner'>
<AccountPanel />
{/* <div className='vizality-main-navigation-account-avatar-container'>
<div className='vizality-main-navigation-account-avatar image-33JSyf' style={{ backgroundImage: `url(${this.userAvatarURL})` }}></div>
</div>
<div className='vizality-main-navigation-account-details-outer'>
<div className='vizality-main-navigation-account-details-inner'>
<div className='vizality-main-navigation-account-username-container'>
<span className='vizality-main-navigation-account-username'>{this.username}</span>
</div>
<div class='vizality-main-navigation-account-status-container'>
<div className={`${this.userStatus} vizality-main-navigation-account-status-indicator`}></div>
<div className='vizality-main-navigation-account-activity-container'>
<span className='vizality-main-navigation-account-activity'>{this.activity ? this.activityTypeText + this.activity.name : ''}</span>
</div>
</div>
</div>
<icon className='tc-icon vizality-main-navigation-account-arrow'></icon>
</div> */}
</div>
</div>
</div>
</nav>
);
}
};

@ -0,0 +1,16 @@
const { React, getModule, getModuleByDisplayName } = require('vizality/webpack');
const { sleep } = require('vizality/util');
const { TextInput, SwitchItem, Category, RadioGroup, SelectInput, CopyInput, TextArea, RegionSelector, SliderInput, PermissionOverrideItem } = require('vizality/components/settings');
class Settings extends React.Component {
render () {
const { getSetting, updateSetting, toggleSetting } = this.props;
return (
<div className='navigation-everywhere'>
</div>
);
}
}
module.exports = Settings;

@ -0,0 +1,49 @@
const { Plugin } = require('vizality/entities');
const { inject, uninject } = require('vizality/injector');
const { React, getModule, getModuleByDisplayName } = require('vizality/webpack');
const { forceUpdateElement } = require('vizality/util');
const Settings = require('./components/Settings');
const MainNav = require('./components/MainNav');
module.exports = class MainNavigation extends Plugin {
startPlugin () {
vizality.api.settings.registerSettings('improved-navigation', {
category: 'improved-navigation',
label: 'improved-navigation',
render: Settings
});
this.loadStylesheet('scss/style.scss');
this._injectMainNav(
this.settings.get('position', 'top'),
this.settings.get('link-style', 'text')
);
}
async _injectMainNav (position = 'top', linkStyle = 'text') {
document.documentElement.setAttribute('vz-main-nav-enabled', '');
document.documentElement.setAttribute('vz-main-nav-position', this.settings.get('position', 'top'));
document.documentElement.setAttribute('vz-main-nav-link-style', this.settings.get('link-style', 'text'));
const { app } = await getModule([ 'app', 'layers' ]);
const Shakeable = await getModuleByDisplayName('Shakeable');
const navBar = React.createElement(MainNav, { position, linkStyle });
inject('vz-mainNav', Shakeable.prototype, 'render', (originalArgs, returnValue) => [ navBar, returnValue ]);
setImmediate(() => forceUpdateElement(`.${app}`));
}
pluginWillUnload () {
const el = document.querySelector('.vizality-main-nav');
if (el) {
el.remove();
}
uninject('vz-mainNav');
}
};

@ -0,0 +1,38 @@
@use '../../../../library/classes/layout/chat' as Chat;
@use '../../../../library/classes/layout/chat' as chat;
@use '../../../../library' as *;
@use '../../../../library/classes';
@use '../../../../library/classes' as class;
@use '../../../../library/icons' as * with (
$announcement: '<svg fill="#fff" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.477 2 2 6.477 2 12C2 17.522 6.477 22 12 22C17.523 22 22 17.522 22 12C22 6.477 17.523 2 12 2ZM8 6C9.104 6 10 6.896 10 8C10 9.105 9.104 10 8 10C6.896 10 6 9.105 6 8C6 6.896 6.896 6 8 6ZM18 14C18 16.617 15.14 19 12 19C8.86 19 6 16.617 6 14V13H18V14ZM16 10C14.896 10 14 9.105 14 8C14 6.896 14.896 6 16 6C17.104 6 18 6.896 18 8C18 9.105 17.104 10 16 10Z" /></svg>'
);
// :root {
// --vz-icon-search: #{svg-uri('<svg fill="#fff" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 1C19.552 1 20 1.447 20 2V3L23 2V6L20 5V6C20 6.553 19.552 7 19 7H15C14.448 7 14 6.553 14 6V2C14 1.447 14.448 1 15 1H19Z" /><path d="M20 13.5V9H22V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5C2 3.397 2.897 2.5 4 2.5H12V4.5H4V13.5H20Z" /></svg>')};
// }
// @use '../../../../library' as *;
#{class('layout.chat.name')} {
color: orange!important;
}
// @use '../../../../library/classes/layout/chat' as Chat;
#{Chat.$name} {
color: red!important;
}
// @use '../../../../library/classes';
#{$layout-chat-name} {
color: teal!important;
}
// @use '../../../../library/classes' as classes;
#{class.$layout-chat-name} {
color: white!important;
}
// @use '../../../../library/classes/layout/chat' as chat;
#{chat.$name} {
color: yellow!important;
}

@ -0,0 +1,313 @@
@use '../../../library' as *;
// @use 'examples';
$module: (
'vz-main-nav': (
background: var('background-floating'),
width: 100%,
height: 50px,
box-shadow: var('elevation-medium'),
border-radius: 0,
border-color: transparent,
border-style: none,
border-width: 0,
'logo': (
icon: var('vz-logo-discord'),
color: #fff,
'hover': (
color: var('vz-main-nav-logo-icon-color'),
animation: 'vz-main-nav-logo-hover .2s forwards'
),
'active': (
color: var('vz-main-nav-logo-icon-color'),
animation: 'vz-main-nav-logo-active .1s forwards'
)
),
'icon': (
color: var('text-normal'),
'hover': (
color: var('vz-main-nav-link-hover-color')
)
),
'link': (
color: var('text-normal'),
margin: 0,
padding: 0 15px,
font-size: 12px,
font-weight: 700,
text-transform: uppercase,
'hover': (
color: #fff,
),
'active': (
color: var('vz-main-nav-link-hover-color'),
)
),
'search': (
background: var('background-tertiary'),
color: var('text-normal'),
box-shadow: var('elevation-medium'),
border-radius: 4px,
border-color: transparent,
border-style: none,
border-width: 0,
'placeholder': (
color: var('text-muted')
),
'focus': (
background: var('vz-main-nav-search-background'),
color: var('vz-main-nav-search-color'),
box-shadow: var('vz-main-nav-search-box-shadow'),
border-radius: var('vz-main-nav-search-border-radius'),
border-color: var('vz-main-nav-search-border-color'),
border-style: var('vz-main-nav-search-border-style'),
border-width: var('vz-main-nav-search-border-width'),
)
)
)
);
// Generating CSS variables from map . . .
:root { @include generate-css-vars($module); }
:root {
--vz-main-nav-user-avatar-border-radius: #{var('vz-user-border-radius')};
}
// Vizality main navigation bar
.vizality-main-nav {
@include size(var('vz-main-nav-width'), var('vz-main-nav-height'));
position: relative;
display: flex;
align-items: stretch;
flex-wrap: nowrap;
// flex: 0 0 var'vz-main-nav-height');
background: var('vz-main-nav-background');
border-radius: var('vz-main-nav-border-radius');
border-width: var('vz-main-nav-border-width');
border-style: var('vz-main-nav-border-style');
border-color:var('vz-main-nav-border-color');
box-shadow:var('vz-main-nav-box-shadow');
z-index: 999;
&__section-left {
display: flex;
flex: 1 1;
flex-flow: row nowrap;
align-items: stretch;
justify-content: flex-start;
width: 100%;
}
&__logo {
@include size(70px, var('vz-main-nav-height'));
@include mask(var('vz-main-nav-logo-icon'), var('vz-main-nav-logo-color'), $size: 50%);
position: relative;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 99;
&:hover {
animation: var('vz-main-nav-logo-hover-animation');
}
&:active {
animation: var('vz-main-nav-logo-active-animation');
}
}
&__link {
position: relative;
display: flex;
flex: 0 0 auto;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 18px;
height: 100%;
text-decoration: none;
cursor: pointer;
&.disabled {
opacity: .5;
pointer-events: none !important;
}
&.active {
}
&:hover {
}
&-text {
margin: var('vz-main-nav-link-margin');
padding: var('vz-main-nav-link-padding');
font-size: var('vz-main-nav-link-font-size');
font-weight: var('vz-main-nav-link-font-weight');
color: var('vz-main-nav-link-color');
text-transform: var('vz-main-nav-link-text-transform');
line-height: 1.5;
white-space: nowrap;
transition: color .3s;
}
&-icon-wrapper {
@include size(37px, 50px);
display: none;
opacity: 0.5;
}
}
&__section-middle {
display: flex;
flex: 1 1;
align-items: center;
justify-content: center;
width: 100%;
}
&__search {
position: relative;
display: flex;
flex-basis: 400px;
margin: 0 20px;
&-inner {
display: flex;
flex-basis: 100%;
}
&-input {
position: relative;
box-sizing: border-box;
display: block;
width: 100%;
height: 24px;
padding: 5px 30px 5px 10px;
font-size: 14px;
line-height: 1.5;
color: var(--text-normal);
border: none;
background-color: var(--background-tertiary);
background-clip: padding-box;
border-radius: 4px;
outline: 0;
&::-webkit-input-placeholder {
color: var(--text-muted);
}
}
&-icon-wrapper {
@include size(24px);
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
}
}
&__section-right {
display: flex;
flex: 1 1;
flex-flow: row nowrap;
align-items: center;
justify-content: flex-end;
width: 100%;
}
&__inbox {
position: relative;
display: flex;
flex: 0 0 auto;
flex-direction: column;
align-items: center;
justify-content: center;
width: 30px;
min-width: 30px;
height: 100%;
margin-right: 5px;
cursor: pointer;
}
&__account-panel {
position: relative;
display: flex;
flex: none;
flex-direction: column;
align-items: center;
justify-content: center;
width: 240px;
cursor: pointer;
&-panel-inner {
position: relative;
display: flex;
flex: 1 1 0%;
flex-flow: row nowrap;
align-items: center;
justify-content: flex-start;
width: 100%;
}
}
}
@keyframes vz-main-nav-logo-hover {
0% {
transform: scale(-1, 1);
animation-timing-function: cubic-bezier(.34, 0, .2, 1);
}
50% {
transform: scale(-1.09, 1.09);
animation-timing-function: cubic-bezier(.67, 0, .71, 1);
}
100% {
transform: scale(-1.08, 1.08);
}
}
@keyframes vz-main-nav-logo-active {
0% {
transform: scale(1.08, 1.08);
animation-timing-function: cubic-bezier(.17, .11, .6, .83);
}
33% {
transform: scale(.87, .87);
animation-timing-function: cubic-bezier(.04, .25, .61, 1.01);
}
100% {
transform: scale(.85, .85);
}
}
// Guilds container
.guilds-1SWlCJ {
// Guild create/join button
.tutorialContainer-SGrQ1h {
// Guild discovery button
+ .listItem-2P_4kh {
display: none;
}
}
// Guild home button
.tutorialContainer-1v44GL {
display: none;
}
.listItem-2P_4kh {
// Guild separator
&.vz-isGuildSeparator {
display: none;
// Guild separator when unread DMs are present
@at-root div:not(.tutorialContainer-1v44GL) + & {
display: flex;
}
}
}
.scroller-2FKFPG {
padding-top: 8px;
}
}
.container-3baos1 {
@include size(100%);
margin: 0;
padding: 0 10px;
box-sizing: border-box;
@at-root .sidebar-2K8pFh & {
display: none;
}
}
[vz-main-nav-position='top'] {
.sidebar-2K8pFh {
border-radius: 0!important;
}
}

@ -0,0 +1,7 @@
{
"name": "Navigation Everywhere",
"version": "0.0.1",
"description": "Adds account panel and navigation bar to the top, right, bottom, or left of the screen. Optionally, include the navigation and account panel within the servers list instead.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,57 @@
const { Plugin } = require('vizality/entities');
const { getModule, React, i18n: { Messages } } = require('vizality/webpack');
const { inject, uninject } = require('vizality/injector');
const PersonPlay = getModule(m => m.id && m.keys().includes('./Activity'), false)('./PersonPlay').default;
module.exports = class GameActivityToggle extends Plugin {
async startPlugin () {
const classes = await getModule([ 'status', 'description' ]);
const settings = await getModule([ 'updateRemoteSettings' ]);
let { showCurrentGame } = await getModule([ 'showCurrentGame' ]);
const Menu = await getModule(m => m.default && m.default.displayName === 'Menu');
inject('game-activity-toggle', Menu, 'default', (originalArgs) => {
if (originalArgs[0].navId !== 'status-picker') {
return originalArgs;
}
const [ { children } ] = originalArgs;
const onlineStatus = children.find(c => c.props.id === 'online');
// Remove the divider after 'Online' because why is there one there anyway, Discord?
if (!Object.keys(children[children.indexOf(onlineStatus) + 1].props).length) {
children.splice(children.indexOf(onlineStatus) + 1, 1);
}
const invisibleStatus = children.find(c => c.props.id === 'invisible');
if (!children.find(c => c.props.id === 'game-activity')) {
children.splice(children.indexOf(invisibleStatus) + 1, 0, React.createElement(Menu.MenuItem, {
id: 'game-activity',
keepItemStyles: true,
action: () => {
showCurrentGame = !showCurrentGame;
return settings.updateRemoteSettings({ showCurrentGame });
},
render: () => React.createElement('div', {
className: classes.statusItem,
'aria-label': `${!showCurrentGame ? 'Show' : 'Hide'} Game Activity`
}, React.createElement(PersonPlay), React.createElement('div', {
className: classes.status
}, `${!showCurrentGame ? 'Show' : 'Hide'} Game Activity`), React.createElement('div', {
className: classes.description
}, Messages.SHOW_CURRENT_GAME))
}));
}
return originalArgs;
}, true);
}
pluginWillUnload () {
uninject('game-activity-toggle');
}
};

@ -0,0 +1,7 @@
{
"name": "Improved Status Picker",
"version": "0.0.1",
"description": "Adds extra functionality to the status picker popout.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -0,0 +1,345 @@
const { React, getModule, getModuleByDisplayName } = require('vizality/webpack');
const { sleep, forceUpdateElement } = require('vizality/util');
const { TextInput, SwitchItem, Category, RadioGroup, SelectInput, ColorPicker, CopyInput, TextArea, RegionSelector, SliderInput, PermissionOverrideItem } = require('vizality/components/settings');
const { clipboard } = require('electron');
let classes = {
initialized: false,
flexClassName: '',
classModule: {},
classDivider: '',
classDividerDef: '',
formModule: {}
};
module.exports = class Settings extends React.Component {
constructor (props) {
super(props);
this.state = {
copyInput: {
mode: getModule(m => m.default && m.default.Modes, false).default.Modes.DEFAULT,
text: 'Copy'
},
classes
};
}
// eslint-disable-next-line no-empty-function
async componentDidMount () {
if (classes.initialized) {
return;
}
const Flex = await getModuleByDisplayName('Flex');
classes = {
initialized: true,
flexClassName: `${Flex.Direction.HORIZONTAL} ${Flex.Justify.START} ${Flex.Align.STRETCH} ${Flex.Wrap.NO_WRAP}`
};
this.setState({ classes });
}
render () {
const { getSetting, updateSetting, toggleSetting } = this.props;
return (
<div className='advanced-titlebar'>
<Category
name={'Titlebar Header'}
description={'Some options for displaying text in the top left of the titlebar.'}
opened={getSetting('header', false)}
onChange={() => toggleSetting('header')}
>
<SwitchItem
note='Show the titlebar header text in the upper left corner.'
value={getSetting('showHeader', true)}
onChange={() => {
toggleSetting('showHeader');
setImmediate(() => forceUpdateElement('.vizality-titlebar'));
}}
>
Show titlebar header text
</SwitchItem>
<TextInput
note='Change the titlebar header text.'
defaultValue={getSetting('headerText', 'Discord')}
required={false}
disabled={!getSetting('showHeader', true)}
onChange={val => {
updateSetting('headerText', val);
setImmediate(() => forceUpdateElement('.vizality-titlebar'));
}}
>
Titlebar header text
</TextInput>
{/* @TODO: Implement this
<SwitchItem
note='Choose a font for the titlebar header text.'
value={getSetting('headerFont', true)}
onChange={() => toggleSetting('headerFont')}
>
Titlebar header text font
</SwitchItem> */}
</Category>
<RadioGroup
required={false}
onChange={val => {
updateSetting('type', val.value);
}}
value={getSetting('type', 'windows')}
options={[
{
name: 'Windows',
value: 'windows'
},
{
name: 'Mac',
value: 'mac'
},
{
name: 'None',
value: 'none',
desc: 'Forcefully remove the titlebar.'
}
]}
>
Titlebar Style
</RadioGroup>
<SwitchItem
note='Shows forward, back, and refresh buttons in the top left.'
value={getSetting('showExtras', false)}
onChange={() => {
toggleSetting('showExtras');
setImmediate(() => forceUpdateElement('.vizality-titlebar'));
}}
>
Extra buttons
</SwitchItem>
<SelectInput
note={'This is a select input.'}
value={getSetting('cm-font-family', 'monaco')}
options={[
{
value: 'monaco',
label: 'Monaco'
},
{
value: 'hack',
label: 'Hack'
},
{
value: 'inconsolata',
label: 'Inconsolata'
},
{
value: 'source-code-pro',
label: 'Source Code Pro'
}
]}
onChange={ret => {
updateSetting('cm-font-family', ret.value);
}}
>
Font Family
</SelectInput>
<ColorPickerInput
colors={[ 1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306 ]}
defaultColor={10070709}
value={getSetting('cake')}
onChange={ret => {
updateSetting('cake', ret);
}}
>
Just a simple color picker.
</ColorPickerInput>
<div className='pie'
style={{
height: 50,
width: 50,
marginBottom: 20,
borderRadius: 1000,
backgroundColor: getModule([ 'int2hex', 'getDarkness', 'isValidHex' ], false).int2hex(getSetting('cake'))
}}>
</div>
<CopyInput
value={'Just some more pie'}
mode={this.state.copyInput.mode}
text={this.state.copyInput.text}
onCopy={async val => {
/*
* for some reason, this selects the text in the input
* so let's clear the selection
*/
window.getSelection().removeAllRanges();
clipboard.writeText(val);
window.getSelection().removeAllRanges();
const modeModule = (await getModule(m => m.default && m.default.Modes)).default.Modes;
this.setState(state => state.copyInput.mode = modeModule.SUCCESS);
this.setState(state => state.copyInput.text = 'Copied');
await sleep(300);
this.setState(state => state.copyInput.mode = modeModule.DEFAULT);
this.setState(state => state.copyInput.text = 'Copy');
}}
>
Just a simple copy input.
</CopyInput>
<TextArea
autofocus={false}
autosize={false}
disabled={false}
flex={false}
maxLength={120}
name={''}
onChange={val => {
updateSetting('textarea-test', val);
val -= getSetting('textarea-test').length;
}}
placeholder={'The best placeholder you ever saw'}
resizeable={false}
rows={3}
value={getSetting('textarea-test')}
>
Just a simple textarea.
</TextArea>
<div className={classes.flexClassName}>
<div className={getModule([ 'flexChild' ], false)} style={{ flex: '1 1 50%' }}>
<SelectInput
note={'This is a select input.'}
value={getSetting('cm-font-family', 'monaco')}
options={[
{
value: 'monaco',
label: 'Monaco'
},
{
value: 'hack',
label: 'Hack'
},
{
value: 'inconsolata',
label: 'Inconsolata'
},
{
value: 'source-code-pro',
label: 'Source Code Pro'
}
]}
onChange={ret => {
updateSetting('cm-font-family', ret.value);
}}
>
Font Family
</SelectInput>
</div>
<div className={getModule([ 'flexChild' ], false)} style={{ flex: '1 1 50%' }}>
<SelectInput
note={'This is a select input.'}
value={getSetting('cm-font-family', 'monaco')}
options={[
{
value: 'monaco',
label: 'Monaco'
},
{
value: 'hack',
label: 'Hack'
},
{
value: 'inconsolata',
label: 'Inconsolata'
},
{
value: 'source-code-pro',
label: 'Source Code Pro'
}
]}
onChange={ret => {
updateSetting('cm-font-family', ret.value);
}}
>
Font Family
</SelectInput>
</div>
</div>
<SliderInput
disabled={false}
note={'This is a slider input.'}
stickToMarkers
initialValue={15}
markers={[ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26 ]}
onMarkerRender={s => `${s}px`}
defaultValue={getSetting('cm-fontSize', 15)}
onChange={v => {
updateSetting('cm-fontSize', v);
}}
>
Font Size
</SliderInput>
{/* <Checkbox
// eslint-disable-next-line multiline-comment-style
// align={'alignCenter-MrlN6q'}
// color={'#7289da'}
text={'is it working'}
disabled={false}
onChange={() => {
toggleSetting('checkbox-test');
}}
readOnly={false}
reverse={false}
// shape={'box-mmYMsp'}
size={24}
type={'inverted'}
value={getSetting('checkbox-test')}
>
Just a simple checkbox.
</Checkbox> */}
{/* <RegionSelector
disabled={false}
error={false}
onClick={ret => {
console.log(ret);
getModule([ 'RegionSelectModal' ], false)({
onChange () {
console.log(...arguments);
}
});
}}
region={{
custom: false,
deprecated: false,
id: 'us-south',
name: 'US South',
optimal: true,
vip: false
}}
>
Just a simple region selector.
</RegionSelector> */}
<PermissionOverrideItem
disabled={false}
hideBorder={false} // divider
note={'Members with this permission can change the channel\'s name or delete it.'}
onChange={''}
value={'ALLOW'} // 'DENY', 'ALLOW', 'PASSTHROUGH'
>
Just a simple permission override item.
</PermissionOverrideItem>
</div>
);
}
};

@ -0,0 +1,81 @@
const webContents = require('electron').remote.getCurrentWebContents();
const { React, getModule } = require('vizality/webpack');
const { Icon} = require('vizality/components');
module.exports = class Titlebar extends React.Component {
render () {
const { type, headerText, showExtras } = this.props;
return (
<>
{type !== 'none' && <titlebar className={`vizality-titlebar ${type}`}>
<div className='vizality-titlebar__section-left'>
{headerText && <div className='vizality-titlebar__header'>
<span className='vizality-titlebar__header-text'>
{headerText}
</span>
</div>}
{showExtras && <div
className={`vizality-titlebar__button-container back small ${!webContents.canGoBack() ? 'disabled' : ''}`}
title='Back'
onClick={() => {
getModule('history', false).history.back();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='caret-left'></Icon>
</div>}
{showExtras && <div
className={`vizality-titlebar__button-container forward small ${!webContents.canGoForward() ? 'disabled' : ''}`}
title='Forward'
onClick={() => {
getModule('history', false).history.forward();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='caret-right'></Icon>
</div>}
{showExtras && <div
className='vizality-titlebar__button-container reload small'
title='Reload'
onClick={() => {
window.reloadElectronApp();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='reload'></Icon>
</div>}
</div>
<div className='vizality-titlebar__section-middle'></div>
<div className='vizality-titlebar__section-right'>
<div
className='vizality-titlebar__button-container minimize'
onClick={() => {
DiscordNative.window.minimize();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='minimize'></Icon>
</div>
<div
className='vizality-titlebar__button-container maximize'
onClick={() => {
DiscordNative.window.maximize();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='maximize'></Icon>
</div>
<div
className='vizality-titlebar__button-container close'
onClick={() => {
DiscordNative.window.close();
}}
>
<Icon wrapperClassName='vizality-titlebar__icon-wrapper' type='close'></Icon>
</div>
</div>
</titlebar>}
</>
);
}
};

@ -0,0 +1,45 @@
const { Plugin } = require('vizality/entities');
const { inject, uninject } = require('vizality/injector');
const { React, getModule, getModuleByDisplayName } = require('vizality/webpack');
const { forceUpdateElement } = require('vizality/util');
const Settings = require('./components/Settings');
const Titlebar = require('./components/Titlebar');
module.exports = class ImprovedTitlebar extends Plugin {
startPlugin () {
vizality.api.settings.registerSettings('improved-titlebar', {
category: 'improved-titlebar',
label: 'improved-titlebar',
render: Settings
});
this.loadStylesheet('style.scss');
this._injectTitlebar(
this.settings.get('type', 'windows'),
this.settings.get('showHeader', true),
this.settings.get('headerText', 'Vizality'),
this.settings.get('showExtras', false)
);
}
async _injectTitlebar (type, showHeader, headerText, showExtras) {
const { app } = getModule([ 'app', 'layers' ]);
document.documentElement.setAttribute('titlebar-type', this.settings.get('type', 'windows'));
const Shakeable = await getModuleByDisplayName('Shakeable');
const titlebar = React.createElement(Titlebar, { type, showHeader, headerText, showExtras });
inject('advancedTitlebar-titlebar', Shakeable.prototype, 'render', (originalArgs, returnValue) => [ titlebar, returnValue ]);
setImmediate(() => forceUpdateElement(`.${app}`));
}
pluginWillUnload () {
const el = document.querySelector('.vizality-titlebar');
if (el) el.remove();
uninject('advancedTitlebar-titlebar');
/*
* @todo: impl; re-render normal titlebar (so none if linux)
*/
}
};

@ -0,0 +1,169 @@
@use '../../library/index' as *;
@font-face {
font-family: "Uni Sans Heavy";
src: url("//db.onlinewebfonts.com/t/4f6fb2fa3c231278167b36e966718cbb.woff2") format("woff2"),
url("//db.onlinewebfonts.com/t/4f6fb2fa3c231278167b36e966718cbb.woff") format("woff");
}
$caret-left: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="transform: rotate(90deg);"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 10L12 15 17 10"></path></svg>';
$caret-right: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="transform: rotate(-90deg);"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M7 10L12 15 17 10"></path></svg>';
$app-reload: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M4 8a4 4 0 0 0 6.828 2.828l1.415 1.415A6 6 0 1 1 12 3.528V2h2v5H9V5h1.646A4 4 0 0 0 4 8z" /></svg>';
$app-minimize: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><rect fill="currentColor" width="10" height="1" x="1" y="6"></rect></svg>';
$app-maximize: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><rect width="9" height="9" x="1.5" y="1.5" fill="none" stroke="currentColor"></rect></svg>';
$app-close: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><polygon fill="currentColor" fill-rule="evenodd" points="11 1.576 6.583 6 11 10.424 10.424 11 6 6.583 1.576 11 1 10.424 5.417 6 1 1.576 1.576 1 6 5.417 10.424 1"/></svg>';
:root {
--titlebar-background: var(--background-tertiary);
--titlebar-height: 22px;
--titlebar-button-width: 28px;
--titlebar-button-small-width: 28px;
--titlebar-button-background: transparent;
--titlebar-button-hover-background: var(--background-modifier-hover);
--titlebar-button-back-size: 18px;
--titlebar-button-forward-size: 18px;
--titlebar-button-reload-size: 14px;
--titlebar-button-minimize-size: 12px;
--titlebar-button-maximize-size: 12px;
--titlebar-button-close-size: 12px;
--titlebar-button-icon-color: var(--interactive-normal);
--titlebar-button-hover-icon-color: var(--background-modifier-hover);
--titlebar-header-text-color: var(--text-muted);
--titlebar-header-font-size: 14px;
--titlebar-header-font: 'Uni Sans Heavy';
--titlebar-icon-back: #{svg-uri($caret-left)};
--titlebar-icon-forward: #{svg-uri($caret-right)};
--titlebar-icon-reload: #{svg-uri($app-reload)};
--titlebar-icon-minimize: #{svg-uri($app-minimize)};
--titlebar-icon-maximize: #{svg-uri($app-maximize)};
--titlebar-icon-close: #{svg-uri($app-close)};
// @todo: Fix up all these CSS and use global var(--icon 's instead
}
.bg-h5JY_x,
.standardSidebarView-3F1I7i {
top: 0!important;
}
.layer-3QrUeG {
top: 0!important;
padding: 0!important;
}
.vizality-titlebar {
height: var(--titlebar-height);
position: relative;
display: flex;
justify-content: space-between;
margin: 0;
background: var(--titlebar-background);
z-index: 102;
&-section-left,
&-section-middle,
&-section-right {
z-index: 1001;
}
&-section-left,
&-section-right {
display: flex;
flex: 0 0 auto;
}
&-section-middle {
display: flex;
flex: 1;
margin: 4px 0 0 0;
-webkit-app-region: drag;
}
&-header {
display: flex;
align-items: center;
margin: 0 10px;
}
&-header-text {
text-transform: uppercase;
font-family: var(--titlebar-header-font);
font-size: var(--titlebar-header-font-size);
color: var(--titlebar-header-text-color);
}
&-button-container {
width: var(--titlebar-button-width);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer !important;
icon {
background-color: var(--titlebar-button-icon-color);
}
&.small {
width: var(--titlebar-button-small-width);
}
&.disabled {
pointer-events: none !important;
opacity: 0.3;
}
&:hover {
background-color: var(--titlebar-button-hover-background);
}
&.back {
icon.back {
@include size(var(--titlebar-button-back-size));
@include mask(var(--titlebar-icon-back));
}
}
&.forward {
icon.forward {
@include size(var(--titlebar-button-forward-size));
@include mask(var(--titlebar-icon-forward));
}
}
&.reload {
icon.reload {
@include size(var(--titlebar-button-reload-size));
@include mask(var(--titlebar-icon-reload));
}
}
&.minimize {
icon.minimize {
@include size(var(--titlebar-button-minimize-size));
@include mask(var(--titlebar-icon-minimize));
}
}
&.maximize {
icon.maximize {
@include size(var(--titlebar-button-maximize-size));
@include mask(var(--titlebar-icon-maximize));
@at-root [data-window-maximized] & {
@include mask(icon('app-restore'));
}
}
}
&.close {
&:hover {
background-color: #f04747;
icon.close {
background-color: #fff;
}
}
icon.close {
@include size(var(--titlebar-button-close-size));
@include mask(var(--titlebar-icon-close));
}
}
}
@at-root [vz-titlebar-type='none'] & {
display: none;
}
}

@ -0,0 +1,7 @@
{
"name": "Advanced Titlebar",
"version": "0.0.1",
"description": "Replaces the default titlebar and gives you some extra options.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -35,7 +35,7 @@ class Base extends React.Component {
renderButtons () {
return (
<div className='buttons'>
{vizality.api.labs.isExperimentEnabled('vz-module-manager-store')
{vizality.api.labs.isExperimentEnabled('vz-store')
? <Button onClick={() => this.goToStore()}>{Messages[`VIZALITY_${this.state.key}_EXPLORE`]}</Button>
: <Tooltip text={Messages.COMING_SOON}>
<Button disabled>{Messages[`VIZALITY_${this.state.key}_EXPLORE`]}</Button>

@ -43,17 +43,6 @@ class Themes extends Base {
}
renderBody () {
if (!vizality.api.labs.isExperimentEnabled('vz-module-manager-themes2')) {
return (
<div className='vizality-plugin-soon vizality-text'>
<div className='wumpus'>
<img src='/assets/8c998f8fb62016fcfb4901e424ff378b.svg' alt='wumpus'/>
</div>
<p>{Messages.VIZALITY_THEMES_WIP1}</p>
<p>{Messages.VIZALITY_THEMES_WIP2}</p>
</div>
);
}
return super.renderBody();
}

@ -24,19 +24,7 @@ module.exports = class ModuleManager extends Plugin {
Object.values(commands).forEach(cmd => vizality.api.commands.registerCommand(cmd));
vizality.api.labs.registerExperiment({
id: 'vz-module-manager-themes2',
name: 'New themes features',
date: 1587857509321,
description: 'New Theme management UI & settings',
callback: () => {
// We're supposed to do it properly but reload > all
setImmediate(() => vizality.pluginManager.remount(this.entityID));
// And we wrap it in setImmediate to not break the labs UI
}
});
vizality.api.labs.registerExperiment({
id: 'vz-module-manager-store',
id: 'vz-store',
name: 'Vizality Store',
date: 1571961600000,
description: 'Vizality Plugin and Theme store',
@ -48,7 +36,7 @@ module.exports = class ModuleManager extends Plugin {
});
vizality.api.labs.registerExperiment({
id: 'vz-module-manager-deeplinks',
id: 'vz-deeplinks',
name: 'Deeplinks',
date: 1590242558077,
description: 'Makes some vizality.com links trigger in-app navigation, as well as some potential embedding if applicable',
@ -64,12 +52,12 @@ module.exports = class ModuleManager extends Plugin {
this._loadQuickCSS();
this._injectSnippets();
this.loadStylesheet('scss/style.scss');
vizality.api.settings.registerSettings('vz-module-manager-plugins', {
vizality.api.settings.registerSettings('vz-plugins', {
category: this.entityID,
label: () => Messages.VIZALITY_PLUGINS,
render: Plugins
});
vizality.api.settings.registerSettings('vz-module-manager-themes', {
vizality.api.settings.registerSettings('vz-themes', {
category: this.entityID,
label: () => Messages.VIZALITY_THEMES,
render: (props) => React.createElement(Themes, {
@ -78,11 +66,11 @@ module.exports = class ModuleManager extends Plugin {
})
});
if (vizality.api.labs.isExperimentEnabled('vz-module-manager-deeplinks')) {
if (vizality.api.labs.isExperimentEnabled('vz-deeplinks')) {
deeplinks();
}
if (vizality.api.labs.isExperimentEnabled('vz-module-manager-store')) {
if (vizality.api.labs.isExperimentEnabled('vz-store')) {
this._injectCommunityContent();
vizality.api.router.registerRoute({
path: '/store/plugins',
@ -101,11 +89,10 @@ module.exports = class ModuleManager extends Plugin {
document.querySelector('#vizality-quickcss').remove();
vizality.api.router.unregisterRoute('/store/plugins');
vizality.api.router.unregisterRoute('/store/themes');
vizality.api.settings.unregisterSettings('vz-module-manager-plugins');
vizality.api.settings.unregisterSettings('vz-module-manager-themes');
vizality.api.labs.unregisterExperiment('vz-module-manager-store');
vizality.api.labs.unregisterExperiment('vz-module-manager-themes2');
vizality.api.labs.unregisterExperiment('vz-module-manager-deeplinks');
vizality.api.settings.unregisterSettings('vz-plugins');
vizality.api.settings.unregisterSettings('vz-themes');
vizality.api.labs.unregisterExperiment('vz-store');
vizality.api.labs.unregisterExperiment('vz-deeplinks');
Object.values(commands).forEach(cmd => vizality.api.commands.unregisterCommand(cmd.command));
uninject('vz-module-manager-channelItem');
uninject('vz-module-manager-channelProps');

@ -55,15 +55,22 @@ module.exports = class Notices extends Plugin {
forceUpdateElement(`.${app}`);
}
_welcomeNewUser () {
async _welcomeNewUser () {
const store = await getModule([ 'getGuilds' ]);
let alreadyJoined = false;
if (store.getGuilds()[GUILD_ID]) {
alreadyJoined = true;
}
this.sendAnnouncement('vz-first-welcome', {
color: 'green',
message: 'Welcome! Vizality has been successfully injected into your Discord client. Feel free to join our Discord server for announcements, support and more!',
button: {
text: 'Join Server',
text: alreadyJoined ? 'Go to Server' : 'Join Server',
onClick: async () => {
const store = await getModule([ 'getGuilds' ]);
if (store.getGuilds()[GUILD_ID]) {
if (alreadyJoined) {
const channel = await getModule([ 'getLastSelectedChannelId' ]);
const router = await getModule([ 'transitionTo' ]);
// eslint-disable-next-line new-cap

@ -0,0 +1,52 @@
const { Plugin } = require('vizality/entities');
const { inject, uninject } = require('vizality/injector');
const { React, getModule } = require('vizality/webpack');
const { findInReactTree } = require('vizality/util');
const { Tooltip, Icon } = require('vizality/components');
module.exports = class QuickDelete extends Plugin {
async startPlugin () {
const deleteMessage = await getModule([ 'deleteMessage', 'sendMessage' ]);
const MiniPopover = await getModule(m => m.default && m.default.displayName === 'MiniPopover');
const classes = {
...getModule([ 'button', 'wrapper', 'disabled' ], false),
...getModule([ 'icon', 'isHeader' ], false)
};
inject('quick-delete-button', MiniPopover, 'default', (originalArgs, returnValue) => {
const props = findInReactTree(returnValue, r => r && r.canDelete && r.message);
if (!props) return returnValue;
const oType = returnValue.props.children[1].type;
returnValue.props.children[1].type = props => {
const ret = oType(props);
ret.props.children.splice(-1, 0,
React.createElement(
Tooltip,
{
className: classes.button,
text: 'Delete',
position: 'top'
},
React.createElement(Icon, {
wrapperClassName: classes.icon,
type: 'trash',
onClick: () => deleteMessage.deleteMessage(props.channel.id, props.message.id)
})
)
);
return ret;
};
return returnValue;
});
MiniPopover.default.displayName = 'MiniPopover';
}
pluginWillUnload () {
uninject('quick-delete-button');
}
};

@ -0,0 +1,7 @@
{
"name": "Quick Delete",
"version": "0.0.1",
"description": "Adds a delete button to the message mini-popover.",
"author": "Baked",
"license": "GPL-3.0"
}

@ -1,4 +1,5 @@
const { Flux, React, getModule, getModuleByDisplayName, i18n: { Messages } } = require('vizality/webpack');
const { classNames } = require('vizality/util');
const { AsyncComponent, Tooltip, HeaderBar, Clickable, Icons } = require('vizality/components');
const ForceUI = require('./ForceUI');
const SplashScreen = require('./SplashScreen');
@ -34,7 +35,7 @@ class SdkWindow extends React.PureComponent {
renderHeaderBar () {
const { title } = getModule([ 'title', 'chatContent' ], false);
return (
<HeaderBar transparent={false} className={[ title, 'vizality-sdk-header' ].join(' ')}>
<HeaderBar transparent={false} className={classNames(title, 'vizality-sdk-header')}>
{this.renderIcon('Force UI', 'Arch', 'force-ui', 'right')}
{this.renderIcon('Discord Splash Screen', 'Arch', 'splash-screen')}
{this.renderIcon('SDK Settings', 'Gear', 'sdk-settings')}
@ -51,7 +52,7 @@ class SdkWindow extends React.PureComponent {
return (
<Tooltip text={tooltip} position={placement}>
<Clickable
className={[ headerBarClasses.iconWrapper, headerBarClasses.clickable ].join(' ')}
className={classNames(headerBarClasses.iconWrapper, headerBarClasses.clickable)}
onClick={async () => {
if (!id) {
// Consider this is the always on top thing

@ -6,8 +6,6 @@ module.exports = async () => {
const PrivateChannel = await getModuleByDisplayName('PrivateChannel');
inject('vz-utility-classes-dmChannels', PrivateChannel.prototype, 'render', (originalArgs, returnValue) => {
if (!returnValue.props) return returnValue;
const { props } = returnValue;
props['vz-user-name'] = props.name;

@ -4,7 +4,6 @@ const { classNames } = require('vizality/util');
module.exports = async () => {
const GameIcon = await getModule(m => m.default && m.default.displayName === 'GameIcon');
const originalGameIcon = GameIcon.default;
inject('vz-utility-classes-gameIcon', GameIcon, 'default', ([ props ], returnValue) => {
if (!props) return returnValue;
@ -19,7 +18,5 @@ module.exports = async () => {
return returnValue;
});
Object.assign(GameIcon.default, originalGameIcon);
return async () => uninject('vz-utility-classes-gameIcon');
};

@ -0,0 +1,41 @@
const { inject, uninject } = require('vizality/injector');
const { getModule, getModuleByDisplayName } = require('vizality/webpack');
const { findInReactTree, classNames, forceUpdateElement } = require('vizality/util');
module.exports = async () => {
return () => void 0;
const List = await getModuleByDisplayName('List');
inject('vz-improved-navigation-dmChannels', List.prototype, 'renderRow', (originalArgs, returnValue) => {
// const props = findInReactTree(returnValue, n => n.id);
// if (!props.id || props.id !== 'private-channels') return returnValue;
// const test = findInReactTree(props, n => n.key);
// const keys = [ 'friends', 'library', 'nitro' ];
// if (keys.includes(test.key)) {
// console.log('hmm');
// }
console.log(originalArgs);
console.log(returnValue);
return returnValue;
});
setImmediate(() => forceUpdateElement('.scroller-2FKFPG'));
// return async () => uninject('vz-improved-navigation-dmChannels');
// const ConnectedPrivateChannelsList = await getModule(m => m.default && m.default.displayName === 'ConnectedPrivateChannelsList');
// inject('vz-improved-navigation-dmChannels', ConnectedPrivateChannelsList, 'default', (originalArgs, returnValue) => {
// console.log(returnValue);
// return returnValue;
// });
return async () => uninject('vz-improved-navigation-dmChannels');
};

@ -37,7 +37,5 @@ module.exports = async () => {
return returnValue;
});
Message.default.displayName = 'Message';
return async () => uninject('vz-utility-classes-messages');
};

@ -4,7 +4,6 @@ const { classNames } = require('vizality/util');
module.exports = async () => {
const CallTile = await getModule(m => m.default && m.default.displayName === 'CallTile');
const originalCallTile = CallTile.default;
inject('vz-utility-classes-privateCall', CallTile, 'default', ([ props ], returnValue) => {
if (!props | !props.participant || !returnValue.props) return returnValue;
@ -26,7 +25,5 @@ module.exports = async () => {
return returnValue;
});
Object.assign(CallTile.default, originalCallTile);
return async () => uninject('vz-utility-classes-privateCall');
};

@ -14,7 +14,11 @@ module.exports = async () => {
inject('vz-utility-classes-tabBar', TabBar.prototype, 'render', function (originalArgs, returnValue) {
if (!returnValue.props || !returnValue.props.children) return returnValue;
const selected = caseify('camel', this.props.selectedItem);
/*
* We check if the item starts with vz- particularly for settings sidebar items
* for core plugins.
*/
const selected = caseify('camel', this.props.selectedItem.startsWith('vz-') ? this.props.selectedItem.replace('vz-', '') : this.props.selectedItem);
returnValue.props['vz-item-selected'] = `vz-${selected}Item`;
@ -23,7 +27,7 @@ module.exports = async () => {
for (const item of tabBarItems) {
if (!item || !item.props || !item.props.id) continue;
const itemFormatted = caseify('camel', item.props.id);
const itemFormatted = caseify('camel', item.props.id.startsWith('vz-') ? item.props.id.replace('vz-', '') : item.props.id);
item.props.className = classNames(item.props.className, `vz-${itemFormatted}Item`);
}

Loading…
Cancel
Save