Last active
October 30, 2023 06:57
-
-
Save samfundev/9f8ef7ad688630ea69bf5052c1415d54 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @name ChannelTabs | |
* @displayName ChannelTabs | |
* @source https://gist.github.com/samfundev/9f8ef7ad688630ea69bf5052c1415d54 | |
* @updateUrl https://gist.githubusercontent.com/samfundev/9f8ef7ad688630ea69bf5052c1415d54/raw | |
* @donate https://paypal.me/samfun123 | |
* @authorId 76052829285916672 | |
*/ | |
/*@cc_on | |
@if (@_jscript) | |
// Offer to self-install for clueless users that try to run this directly. | |
var shell = WScript.CreateObject("WScript.Shell"); | |
var fs = new ActiveXObject("Scripting.FileSystemObject"); | |
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins"); | |
var pathSelf = WScript.ScriptFullName; | |
// Put the user at ease by addressing them in the first person | |
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); | |
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { | |
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); | |
} else if (!fs.FolderExists(pathPlugins)) { | |
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); | |
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { | |
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); | |
// Show the user where to put plugins in the future | |
shell.Exec("explorer " + pathPlugins); | |
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); | |
} | |
WScript.Quit(); | |
@else@*/ | |
module.exports = (() => { | |
const config = { | |
info: { | |
name: "ChannelTabs", | |
authors: [ | |
{ | |
name: "l0c4lh057", | |
discord_id: "226677096091484160", | |
github_username: "l0c4lh057", | |
twitter_username: "l0c4lh057" | |
}, | |
{ | |
name: "CarJem Generations", | |
discord_id: "519397452944769025", | |
github_username: "CarJem", | |
twitter_username: "carter5467_99" | |
}, | |
{ | |
name: "samfundev", | |
discord_id: "76052829285916672", | |
github_username: "samfundev", | |
} | |
], | |
version: "2.6.7", | |
description: "Allows you to have multiple tabs and bookmark channels", | |
github: "https://gist.github.com/samfundev/9f8ef7ad688630ea69bf5052c1415d54", | |
github_raw: "https://gist.githubusercontent.com/samfundev/9f8ef7ad688630ea69bf5052c1415d54/raw" | |
}, | |
changelog: [ | |
{ | |
"title": "Fixed", | |
"type": "fixed", | |
"items": [ | |
"Fixed appearance context causing a crash", | |
"Fixed some context menu options displaying incorrectly", | |
] | |
} | |
] | |
}; | |
return !global.ZeresPluginLibrary ? class { | |
constructor(){ this._config = config; } | |
getName(){ return config.info.name; } | |
getAuthor(){ return config.info.authors.map(a => a.name).join(", "); } | |
getDescription(){ return config.info.description + " **Install [ZeresPluginLibrary](https://betterdiscord.app/Download?id=9) and restart discord to use this plugin!**"; } | |
getVersion(){ return config.info.version; } | |
load(){ | |
BdApi.showConfirmationModal("Library plugin is needed", | |
[`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`], { | |
confirmText: "Download", | |
cancelText: "Cancel", | |
onConfirm: () => { | |
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => { | |
if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9"); | |
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); | |
}); | |
} | |
} | |
); | |
} | |
start(){} | |
stop(){} | |
} : (([Plugin, Api]) => { | |
const plugin = (Plugin, Api) => { | |
//#region Module/Variable Definitions | |
const { WebpackModules, PluginUtilities, DiscordModules, ReactComponents, ReactTools, Settings, Modals } = Api; | |
const { React, NavigationUtils, SelectedChannelStore, SelectedGuildStore, ChannelStore, GuildStore, UserStore, UserTypingStore, Permissions } = DiscordModules; | |
const { ContextMenu, Patcher, Webpack } = new BdApi("ChannelTabs"); | |
const DiscordConstants = { | |
ChannelTypes: Webpack.getModule(Webpack.Filters.byProps("GUILD_TEXT"), { searchExports: true }) | |
}; | |
const Textbox = WebpackModules.find(m => m.defaultProps && m.defaultProps.type == "text", { searchExports: true }); | |
const UnreadStateStore = WebpackModules.find(m => m.isEstimated); | |
const Flux = WebpackModules.getByProps("connectStores"); | |
const MutedStore = WebpackModules.getByProps("isMuted", "isChannelMuted"); | |
const PermissionUtils = WebpackModules.getByProps("can", "canManageUser"); | |
const UserStatusStore = DiscordModules.UserStatusStore; | |
const Spinner = WebpackModules.getModule(m => m.toString().includes("spinningCircle")); | |
const Tooltip = WebpackModules.getModule((m) => m?.toString().includes("shouldShowTooltip") && m?.Positions); | |
const Slider = WebpackModules.getModule(m => m?.toString().includes(`"[UIKit]Slider.handleMouseDown(): assert failed: domNode nodeType !== Element"`), { searchExports: true }); | |
const NavShortcuts = WebpackModules.getByProps("NAVIGATE_BACK", "NAVIGATE_FORWARD"); | |
const Close = WebpackModules.find(m => m.toString().includes("M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z")); | |
const PlusAlt = WebpackModules.find(m => m.toString().includes("15 10 10 10 10 15 8 15 8 10 3 10 3 8 8 8 8 3 10 3 10 8 15 8")); | |
const LeftCaret = WebpackModules.find(m => m.toString().includes("18.35 4.35 16 2 6 12 16 22 18.35 19.65 10.717 12")); | |
const RightCaret = WebpackModules.find(m => m.toString().includes("8.47 2 6.12 4.35 13.753 12 6.12 19.65 8.47 22 18.47 12")); | |
const DefaultUserIconGrey = "https://cdn.discordapp.com/embed/avatars/0.png"; | |
const DefaultUserIconGreen = "https://cdn.discordapp.com/embed/avatars/1.png"; | |
const DefaultUserIconBlue = "https://cdn.discordapp.com/embed/avatars/2.png"; | |
const DefaultUserIconRed = "https://cdn.discordapp.com/embed/avatars/3.png"; | |
const DefaultUserIconYellow = "https://cdn.discordapp.com/embed/avatars/4.png"; | |
const SettingsMenuIcon = `<svg class="channelTabs-settingsIcon" aria-hidden="false" viewBox="0 0 80 80"> | |
<rect fill="var(--interactive-normal)" x="20" y="15" width="50" height="10"></rect> | |
<rect fill="var(--interactive-normal)" x="20" y="35" width="50" height="10"></rect> | |
<rect fill="var(--interactive-normal)" x="20" y="55" width="50" height="10"></rect> | |
</svg>`; | |
var switching = false; | |
var patches = []; | |
var currentTabDragIndex = -1; | |
var currentTabDragDestinationIndex = -1; | |
var currentFavDragIndex = -1; | |
var currentFavDragDestinationIndex = -1; | |
var currentGroupDragIndex = -1; | |
var currentGroupDragDestinationIndex = -1; | |
var currentGroupOpened = -1; | |
//#endregion | |
//#region Context Menu Constructors | |
function CreateGuildContextMenuChildren(instance, props, channel) | |
{ | |
return ContextMenu.buildMenuChildren([{ | |
type: "group", | |
items: [ | |
{ | |
type: "submenu", | |
label: "ChannelTabs", | |
items: instance.mergeItems([ | |
{ | |
label: "Open channel in new tab", | |
action: ()=>TopBarRef.current && TopBarRef.current.saveChannel(props.guild.id, channel.id, "#" + channel.name, props.guild.getIconURL() || "") | |
}, | |
{ | |
label: "Save channel as bookmark", | |
action: ()=>TopBarRef.current && TopBarRef.current.addToFavs("#" + channel.name, props.guild.getIconURL() || "", `/channels/${props.guild.id}/${channel.id}`, channel.id) | |
}], | |
[{ | |
label: "Save guild as bookmark", | |
action: ()=>TopBarRef.current && TopBarRef.current.addToFavs(props.guild.name, props.guild.getIconURL() || "", `/channels/${props.guild.id}`, undefined, props.guild.id) | |
}] | |
) | |
} | |
] | |
}]); | |
}; | |
function CreateTextChannelContextMenuChildren(instance, props) | |
{ | |
return ContextMenu.buildMenuChildren([{ | |
type: "group", | |
items: [ | |
{ | |
type: "submenu", | |
label: "ChannelTabs", | |
items: instance.mergeItems([ | |
{ | |
label: "Open in new tab", | |
action: ()=>TopBarRef.current && TopBarRef.current.saveChannel(props.guild.id, props.channel.id, "#" + props.channel.name, props.guild.getIconURL() || "") | |
}], | |
[{ | |
label: "Save channel as bookmark", | |
action: ()=>TopBarRef.current && TopBarRef.current.addToFavs("#" + props.channel.name, props.guild.getIconURL() || "", `/channels/${props.guild.id}/${props.channel.id}`, props.channel.id) | |
}] | |
) | |
} | |
] | |
}]); | |
}; | |
function CreateDMContextMenuChildren(instance, props) | |
{ | |
return ContextMenu.buildMenuChildren([{ | |
type: "group", | |
items: [ | |
{ | |
type: "submenu", | |
label: "ChannelTabs", | |
items: instance.mergeItems( | |
[{ | |
label: "Open in new tab", | |
action: ()=>TopBarRef.current && TopBarRef.current.saveChannel(props.channel.guild_id, props.channel.id, "@" + (props.channel.name || props.user.username), props.user.getAvatarURL(null, 40, false)) | |
}], | |
[{ | |
label: "Save DM as bookmark", | |
action: ()=>TopBarRef.current && TopBarRef.current.addToFavs("@" + (props.channel.name || props.user.username), props.user.getAvatarURL(null, 40, false), `/channels/@me/${props.channel.id}`, props.channel.id) | |
}] | |
) | |
} | |
] | |
}]) | |
}; | |
function CreateGroupContextMenuChildren(instance, props) | |
{ | |
return ContextMenu.buildMenuChildren([{ | |
type: "group", | |
items: [ | |
{ | |
type: "submenu", | |
label: "ChannelTabs", | |
items: instance.mergeItems( | |
[{ | |
label: "Open in new tab", | |
action: ()=>TopBarRef.current && TopBarRef.current.saveChannel(props.channel.guild_id, props.channel.id, "@" + (props.channel.name || props.channel.rawRecipients.map(u=>u.username).join(", ")), ""/*TODO*/) | |
}], | |
[{ | |
label: "Save bookmark", | |
action: ()=>TopBarRef.current && TopBarRef.current.addToFavs("@" + (props.channel.name || props.channel.rawRecipients.map(u=>u.username).join(", ")), ""/*TODO*/, `/channels/@me/${props.channel.id}`, props.channel.id) | |
}] | |
) | |
} | |
] | |
}]) | |
}; | |
function CreateTabContextMenu(props,e) | |
{ | |
ContextMenu.open( | |
e, | |
ContextMenu.buildMenu([ | |
{ | |
type: "group", | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: "Duplicate", | |
action: props.openInNewTab | |
}, | |
{ | |
label: "Add to favourites", | |
action: ()=>props.addToFavs(props.name, props.iconUrl, props.url, props.channelId) | |
}, | |
{ | |
label: "Minimize tab", | |
type: "toggle", | |
checked: () => props.minimized, | |
action: ()=> props.minimizeTab(props.tabIndex) | |
} | |
] | |
}, | |
{ | |
include: props.tabCount > 1, | |
values: [ | |
{ | |
type : "separator" | |
}, | |
{ | |
label: "Move left", | |
action: props.moveLeft | |
}, | |
{ | |
label: "Move right", | |
action: props.moveRight | |
} | |
] | |
}, | |
{ | |
include: props.tabCount > 1, | |
values: [ | |
{ | |
type : "separator" | |
}, | |
{ | |
type: "submenu", | |
label: "Close...", | |
id: "closeMenu", | |
color: "danger", | |
action: ()=>props.closeTab(props.tabIndex, "single"), | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: "Close tab", | |
action: ()=>props.closeTab(props.tabIndex, "single"), | |
color: "danger" | |
}, | |
{ | |
label: "Close all other tabs", | |
action: ()=>props.closeTab(props.tabIndex, "other"), | |
color: "danger" | |
} | |
] | |
}, | |
{ | |
include: props.tabIndex != props.tabCount - 1, | |
values: [ | |
{ | |
label: "Close all tabs to right", | |
action: ()=>props.closeTab(props.tabIndex, "right"), | |
color: "danger" | |
} | |
] | |
}, | |
{ | |
include: props.tabIndex != 0, | |
values: [ | |
{ | |
label: "Close all tabs to left", | |
action: ()=>props.closeTab(props.tabIndex, "left"), | |
color: "danger" | |
} | |
] | |
} | |
) | |
} | |
] | |
} | |
) | |
} | |
]), | |
{ | |
position: "right", | |
align: "top" | |
} | |
); | |
}; | |
function CreateFavContextMenu(props,e) | |
{ | |
ContextMenu.open( | |
e, | |
ContextMenu.buildMenu([ | |
{ | |
type: "group", | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: "Open in new tab", | |
action: props.openInNewTab | |
}, | |
{ | |
label: "Rename", | |
action: props.rename | |
}, | |
{ | |
label: "Minimize favourite", | |
type: "toggle", | |
checked: () => props.minimized, | |
action: ()=> props.minimizeFav(props.favIndex) | |
}, | |
{ | |
type : "separator" | |
} | |
] | |
}, | |
{ | |
include: props.favCount > 1, | |
values: [ | |
{ | |
label: "Move left", | |
action: props.moveLeft | |
}, | |
{ | |
label: "Move right", | |
action: props.moveRight | |
}, | |
{ | |
type : "separator" | |
} | |
] | |
}, | |
{ | |
values: [ | |
{ | |
label: "Move To...", | |
id: "groupMoveTo", | |
type: "submenu", | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: "Favorites Bar", | |
id: "entryNone", | |
color: "danger", | |
action: () => props.moveToFavGroup(props.favIndex, -1) | |
}, | |
{ | |
type: "separator" | |
} | |
] | |
}, | |
{ | |
values: FavMoveToGroupList({favIndex: props.favIndex, ...props}) | |
} | |
) | |
}, | |
{ | |
type : "separator" | |
} | |
] | |
}, | |
{ | |
values: [ | |
{ | |
label: "Delete", | |
action: props.delete, | |
color: "danger" | |
} | |
] | |
} | |
) | |
} | |
]), | |
{ | |
position: "right", | |
align: "top" | |
} | |
); | |
}; | |
function CreateFavGroupContextMenu(props,e) | |
{ | |
ContextMenu.open( | |
e, | |
ContextMenu.buildMenu([ | |
{ | |
type: "group", | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: "Open all", | |
action: ()=>props.openFavGroupInNewTab(props.favGroup.groupId) | |
}, | |
{ | |
type : "separator" | |
} | |
] | |
}, | |
{ | |
include: props.groupCount > 1, | |
values: [ | |
{ | |
label: "Move left", | |
action: ()=>props.moveFavGroup(props.groupIndex, (props.groupIndex + props.groupCount - 1) % props.groupCount) | |
}, | |
{ | |
label: "Move right", | |
action: ()=>props.moveFavGroup(props.groupIndex, (props.groupIndex + 1) % props.groupCount) | |
}, | |
{ | |
type : "separator" | |
} | |
] | |
}, | |
{ | |
values: [ | |
{ | |
label: "Rename", | |
id: "renameGroup", | |
action: ()=>props.renameFavGroup(props.favGroup.name, props.favGroup.groupId) | |
}, | |
{ | |
type : "separator" | |
}, | |
{ | |
label: "Delete", | |
id: "deleteGroup", | |
action: ()=>props.removeFavGroup(props.favGroup.groupId), | |
color: "danger" | |
} | |
] | |
} | |
) | |
} | |
]), | |
{ | |
position: "right", | |
align: "top" | |
} | |
); | |
}; | |
function CreateFavBarContextMenu(props,e) | |
{ | |
ContextMenu.open( | |
e, | |
ContextMenu.buildMenu([ | |
{ | |
type: "group", | |
items: [ | |
{ | |
label: "Add current tab as favourite", | |
action: ()=>props.addToFavs(getCurrentName(), getCurrentIconUrl(), location.pathname, SelectedChannelStore.getChannelId()) | |
}, | |
{ | |
label: "Create a new group...", | |
action: props.addFavGroup | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
label: "Hide Favorites", | |
action: props.hideFavBar, | |
color: "danger" | |
} | |
] | |
} | |
]), | |
{ | |
position: "right", | |
align: "top" | |
} | |
); | |
}; | |
function CreateSettingsContextMenu(instance, e) | |
{ | |
ContextMenu.open( | |
e, | |
ContextMenu.buildMenu([ | |
{ | |
type: "group", | |
items: mergeLists( | |
{ | |
values: [ | |
{ | |
label: config.info.name, | |
subtext: "Version " + config.info.version, | |
action: () => { | |
Modals.showChangelogModal(config.info.name, config.info.version, config.changelog); | |
} | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
id: "shortcutLabel", | |
disabled: true, | |
label: "Shortcuts:" | |
}, | |
{ | |
id: "shortcutLabelKeys", | |
disabled: true, | |
render: () => { | |
return React.createElement("div", {style: { "color": "var(--text-muted)", "padding": "8px", "font-size": "12px", "white-space": "pre-wrap" }}, | |
`Ctrl + W - Close Current Tab\n` + | |
`Ctrl + PgUp - Navigate to Left Tab\n` + | |
`Ctrl + PgDn - Navigate to Right Tab\n`); | |
} | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
label: "Settings:", | |
id: "settingHeader", | |
disabled: true | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
type: "submenu", | |
label: "Startup", | |
items: [ | |
{ | |
label: "Reopen Last Channel on Startup", | |
type: "toggle", | |
id: "reopenLastChannel", | |
checked: () => TopBarRef.current.state.reopenLastChannel, | |
action: () => { | |
instance.setState({ | |
reopenLastChannel: !instance.state.reopenLastChannel | |
}, ()=>{ | |
instance.props.plugin.settings.reopenLastChannel = !instance.props.plugin.settings.reopenLastChannel; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
} | |
] | |
}, | |
{ | |
type: "submenu", | |
label: "Appearance", | |
items: [ | |
{ | |
label: "Use Compact Appearance", | |
type: "toggle", | |
id: "useCompactLook", | |
checked: () => TopBarRef.current.state.compactStyle, | |
action: () => { | |
instance.setState({ | |
compactStyle: !instance.state.compactStyle | |
}, ()=>{ | |
instance.props.plugin.settings.compactStyle = !instance.props.plugin.settings.compactStyle; | |
instance.props.plugin.removeStyle(); | |
instance.props.plugin.applyStyle(); | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Privacy Mode", | |
type: "toggle", | |
id: "privacyMode", | |
checked: () => TopBarRef.current.state.privacyMode, | |
action: () => { | |
instance.setState({ | |
privacyMode: !instance.state.privacyMode | |
}, ()=>{ | |
instance.props.plugin.settings.privacyMode = !instance.props.plugin.settings.privacyMode; | |
instance.props.plugin.removeStyle(); | |
instance.props.plugin.applyStyle(); | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Radial Status Indicators", | |
type: "toggle", | |
id: "radialStatusMode", | |
checked: () => TopBarRef.current.state.radialStatusMode, | |
action: () => { | |
instance.setState({ | |
radialStatusMode: !instance.state.radialStatusMode | |
}, ()=>{ | |
instance.props.plugin.settings.radialStatusMode = !instance.props.plugin.settings.radialStatusMode; | |
instance.props.plugin.removeStyle(); | |
instance.props.plugin.applyStyle(); | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
label: "Minimum Tab Width", | |
style: { "pointer-events": "none" } | |
}, | |
{ | |
id: "tabWidthMin", | |
render: () => { | |
return React.createElement("div", | |
{ | |
className: "channelTabs-sliderContainer" | |
}, | |
React.createElement(Slider, | |
{ | |
"aria-label": "Minimum Tab Width", | |
className: "channelTabs-slider", | |
mini: true, | |
orientation: "horizontal", | |
disabled: false, | |
initialValue: instance.props.plugin.settings.tabWidthMin, | |
minValue: 50, | |
maxValue: 220, | |
onValueRender: value => Math.floor(value / 10) * 10 + 'px', | |
onValueChange: value => { | |
value = Math.floor(value / 10) * 10, | |
instance.props.plugin.settings.tabWidthMin = value, | |
instance.props.plugin.saveSettings(), | |
instance.props.plugin.applyStyle("channelTabs-style-constants") | |
} | |
} | |
) | |
) | |
} | |
}, | |
{ | |
type: "separator" | |
}, | |
{ | |
label: "Show Tab Bar", | |
type: "toggle", | |
id: "showTabBar", | |
color: "danger", | |
checked: () => TopBarRef.current.state.showTabBar, | |
action: () => { | |
instance.setState({ | |
showTabBar: !instance.state.showTabBar | |
}, ()=>{ | |
instance.props.plugin.settings.showTabBar = !instance.props.plugin.settings.showTabBar; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Fav Bar", | |
type: "toggle", | |
id: "showFavBar", | |
color: "danger", | |
checked: () => TopBarRef.current.state.showFavBar, | |
action: () => { | |
instance.setState({ | |
showFavBar: !instance.state.showFavBar | |
}, ()=>{ | |
instance.props.plugin.settings.showFavBar = !instance.props.plugin.settings.showFavBar; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Quick Settings", | |
type: "toggle", | |
id: "showQuickSettings", | |
color: "danger", | |
checked: () => TopBarRef.current.state.showQuickSettings, | |
action: () => { | |
instance.setState({ | |
showQuickSettings: !instance.state.showQuickSettings | |
}, ()=>{ | |
instance.props.plugin.settings.showQuickSettings = !instance.props.plugin.settings.showQuickSettings; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Navigation Buttons", | |
type: "toggle", | |
id: "showNavButtons", | |
checked: () => TopBarRef.current.state.showNavButtons, | |
action: () => { | |
instance.setState({ | |
showNavButtons: !instance.state.showNavButtons | |
}, ()=>{ | |
instance.props.plugin.settings.showNavButtons = !instance.props.plugin.settings.showNavButtons; | |
instance.props.plugin.removeStyle(); | |
instance.props.plugin.applyStyle(); | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
} | |
] | |
}, | |
{ | |
type: "submenu", | |
label: "Behavior", | |
items: [ | |
{ | |
label: "Always Focus New Tabs", | |
type: "toggle", | |
id: "alwaysFocusNewTabs", | |
checked: () => TopBarRef.current.state.alwaysFocusNewTabs, | |
action: () => { | |
instance.setState({ | |
alwaysFocusNewTabs: !instance.state.alwaysFocusNewTabs | |
}, ()=>{ | |
instance.props.plugin.settings.alwaysFocusNewTabs = !instance.props.plugin.settings.alwaysFocusNewTabs; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Primary Forward/Back Navigation", | |
type: "toggle", | |
id: "useStandardNav", | |
checked: () => TopBarRef.current.state.useStandardNav, | |
action: () => { | |
instance.setState({ | |
useStandardNav: !instance.state.useStandardNav | |
}, ()=>{ | |
instance.props.plugin.settings.useStandardNav = !instance.props.plugin.settings.useStandardNav; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
} | |
] | |
}, | |
{ | |
type: "submenu", | |
label: "Badge Visibility", | |
items: [ | |
{ | |
type: "separator", | |
id: "header1_1" | |
}, | |
{ | |
label: "Favs:", | |
id: "header1_2", | |
disabled: true | |
}, | |
{ | |
type: "separator", | |
id: "header1_3" | |
}, | |
{ | |
label: "Show Mentions", | |
type: "toggle", | |
id: "favs_Mentions", | |
checked: () => TopBarRef.current.state.showFavMentionBadges, | |
action: () => { | |
instance.setState({ | |
showFavMentionBadges: !instance.state.showFavMentionBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showFavMentionBadges = !instance.props.plugin.settings.showFavMentionBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Unreads", | |
type: "toggle", | |
id: "favs_Unreads", | |
checked: () => TopBarRef.current.state.showFavUnreadBadges, | |
action: () => { | |
instance.setState({ | |
showFavUnreadBadges: !instance.state.showFavUnreadBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showFavUnreadBadges = !instance.props.plugin.settings.showFavUnreadBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Typing", | |
type: "toggle", | |
id: "favs_Typing", | |
checked: () => TopBarRef.current.state.showFavTypingBadge, | |
action: () => { | |
instance.setState({ | |
showFavTypingBadge: !instance.state.showFavTypingBadge | |
}, ()=>{ | |
instance.props.plugin.settings.showFavTypingBadge = !instance.props.plugin.settings.showFavTypingBadge; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Empty Mentions/Unreads", | |
type: "toggle", | |
id: "favs_Empty", | |
checked: () => TopBarRef.current.state.showEmptyFavBadges, | |
action: () => { | |
instance.setState({ | |
showEmptyFavBadges: !instance.state.showEmptyFavBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showEmptyFavBadges = !instance.props.plugin.settings.showEmptyFavBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
type: "separator", | |
id: "header4_1" | |
}, | |
{ | |
label: "Fav Groups:", | |
id: "header4_2", | |
disabled: true | |
}, | |
{ | |
type: "separator", | |
id: "header4_3" | |
}, | |
{ | |
label: "Show Mentions", | |
type: "toggle", | |
id: "favGroups_Mentions", | |
checked: () => TopBarRef.current.state.showFavGroupMentionBadges, | |
action: () => { | |
instance.setState({ | |
showFavGroupMentionBadges: !instance.state.showFavGroupMentionBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showFavGroupMentionBadges = !instance.props.plugin.settings.showFavGroupMentionBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Unreads", | |
type: "toggle", | |
id: "favGroups_Unreads", | |
checked: () => TopBarRef.current.state.showFavGroupUnreadBadges, | |
action: () => { | |
instance.setState({ | |
showFavGroupUnreadBadges: !instance.state.showFavGroupUnreadBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showFavGroupUnreadBadges = !instance.props.plugin.settings.showFavGroupUnreadBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Typing", | |
type: "toggle", | |
id: "favGroups_Typing", | |
checked: () => TopBarRef.current.state.showFavGroupTypingBadge, | |
action: () => { | |
instance.setState({ | |
showFavGroupTypingBadge: !instance.state.showFavGroupTypingBadge | |
}, ()=>{ | |
instance.props.plugin.settings.showFavGroupTypingBadge = !instance.props.plugin.settings.showFavGroupTypingBadge; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Empty Mentions/Unreads", | |
type: "toggle", | |
id: "favGroups_Empty", | |
checked: () => TopBarRef.current.state.showEmptyFavGroupBadges, | |
action: () => { | |
instance.setState({ | |
showEmptyFavGroupBadges: !instance.state.showEmptyFavGroupBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showEmptyFavGroupBadges = !instance.props.plugin.settings.showEmptyFavGroupBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
type: "separator", | |
id: "header2_1" | |
}, | |
{ | |
label: "Tabs:", | |
id: "header2_2", | |
disabled: true | |
}, | |
{ | |
type: "separator", | |
id: "header2_3" | |
}, | |
{ | |
label: "Show Mentions", | |
type: "toggle", | |
id: "tabs_Mentions", | |
checked: () => TopBarRef.current.state.showTabMentionBadges, | |
action: () => { | |
instance.setState({ | |
showTabMentionBadges: !instance.state.showTabMentionBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showTabMentionBadges = !instance.props.plugin.settings.showTabMentionBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Unreads", | |
type: "toggle", | |
id: "tabs_Unreads", | |
checked: () => TopBarRef.current.state.showTabUnreadBadges, | |
action: () => { | |
instance.setState({ | |
showTabUnreadBadges: !instance.state.showTabUnreadBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showTabUnreadBadges = !instance.props.plugin.settings.showTabUnreadBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Typing", | |
type: "toggle", | |
id: "tabs_Typing", | |
checked: () => TopBarRef.current.state.showTabTypingBadge, | |
action: () => { | |
instance.setState({ | |
showTabTypingBadge: !instance.state.showTabTypingBadge | |
}, ()=>{ | |
instance.props.plugin.settings.showTabTypingBadge = !instance.props.plugin.settings.showTabTypingBadge; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Empty Mentions/Unreads", | |
type: "toggle", | |
id: "tabs_Empty", | |
checked: () => TopBarRef.current.state.showEmptyTabBadges, | |
action: () => { | |
instance.setState({ | |
showEmptyTabBadges: !instance.state.showEmptyTabBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showEmptyTabBadges = !instance.props.plugin.settings.showEmptyTabBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
type: "separator", | |
id: "header3_1" | |
}, | |
{ | |
label: "Active Tabs:", | |
id: "header3_2", | |
disabled: true | |
}, | |
{ | |
type: "separator", | |
id: "header3_3" | |
}, | |
{ | |
label: "Show Mentions", | |
type: "toggle", | |
id: "activeTabs_Mentions", | |
checked: () => TopBarRef.current.state.showActiveTabMentionBadges, | |
action: () => { | |
instance.setState({ | |
showActiveTabMentionBadges: !instance.state.showActiveTabMentionBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showActiveTabMentionBadges = !instance.props.plugin.settings.showActiveTabMentionBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Unreads", | |
type: "toggle", | |
id: "activeTabs_Unreads", | |
checked: () => TopBarRef.current.state.showActiveTabUnreadBadges, | |
action: () => { | |
instance.setState({ | |
showActiveTabUnreadBadges: !instance.state.showActiveTabUnreadBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showActiveTabUnreadBadges = !instance.props.plugin.settings.showActiveTabUnreadBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Typing", | |
type: "toggle", | |
id: "activeTabs_Typing", | |
checked: () => TopBarRef.current.state.showActiveTabTypingBadge, | |
action: () => { | |
instance.setState({ | |
showActiveTabTypingBadge: !instance.state.showActiveTabTypingBadge | |
}, ()=>{ | |
instance.props.plugin.settings.showActiveTabTypingBadge = !instance.props.plugin.settings.showActiveTabTypingBadge; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
}, | |
{ | |
label: "Show Empty Mentions/Unreads", | |
type: "toggle", | |
id: "activeTabs_Empty", | |
checked: () => TopBarRef.current.state.showEmptyActiveTabBadges, | |
action: () => { | |
instance.setState({ | |
showEmptyActiveTabBadges: !instance.state.showEmptyActiveTabBadges | |
}, ()=>{ | |
instance.props.plugin.settings.showEmptyActiveTabBadges = !instance.props.plugin.settings.showEmptyActiveTabBadges; | |
instance.props.plugin.saveSettings(); | |
}); | |
} | |
} | |
] | |
}, | |
] | |
} | |
) | |
} | |
]), | |
{ | |
position: "right", | |
align: "top" | |
} | |
) | |
}; | |
//#endregion | |
//#region Global Common Functions | |
const closeAllDropdowns = () => | |
{ | |
var dropdowns = document.getElementsByClassName("channelTabs-favGroup-content"); | |
var i; | |
for (i = 0; i < dropdowns.length; i++) { | |
var openDropdown = dropdowns[i]; | |
if (openDropdown.classList.contains('channelTabs-favGroupShow')) { | |
openDropdown.classList.remove('channelTabs-favGroupShow'); | |
} | |
} | |
currentGroupOpened = -1; | |
}; | |
const mergeLists = (...items)=> | |
{ | |
return items.filter(item => item.include===undefined||item.include).flatMap(item => item.values); | |
}; | |
const getGuildChannels = (...guildIds)=> | |
{ | |
const channels = ChannelStore.getGuildChannels ? Object.values(ChannelStore.getGuildChannels()) : ChannelStore.getMutableGuildChannels ? Object.values(ChannelStore.getMutableGuildChannels()) : []; | |
return channels.filter(c => guildIds.includes(c.guild_id) && c.type !== DiscordConstants.ChannelTypes.GUILD_VOICE && c.type !== DiscordConstants.ChannelTypes.GUILD_CATEGORY); | |
}; | |
const updateFavEntry = (fav)=> | |
{ | |
if(fav.guildId) | |
{ | |
const channelIds = getGuildChannels(fav.guildId).filter(channel=>(PermissionUtils.can(Permissions.VIEW_CHANNEL, channel)) && (!MutedStore.isChannelMuted(channel.guild_id, channel.id))).map(channel=>channel.id); | |
return { | |
unreadCount: channelIds.map(id=>UnreadStateStore.getUnreadCount(id)||UnreadStateStore.getMentionCount(id)||(UnreadStateStore.hasUnread(id)?1:0)).reduce((a,b)=>a+b, 0), | |
unreadEstimated: channelIds.some(id=>UnreadStateStore.isEstimated(id)) || channelIds.some(id=>UnreadStateStore.getUnreadCount(id)===0&&UnreadStateStore.hasUnread(id)), | |
hasUnread: channelIds.some(id=>UnreadStateStore.hasUnread(id)), | |
mentionCount: channelIds.map(id=>UnreadStateStore.getMentionCount(id)||0).reduce((a,b)=>a+b, 0), | |
selected: SelectedGuildStore.getGuildId()===fav.guildId, | |
isTyping: isChannelTyping(fav.channelId), | |
currentStatus: getCurrentUserStatus(fav.url) | |
}; | |
} | |
else | |
{ | |
return { | |
unreadCount: UnreadStateStore.getUnreadCount(fav.channelId) || UnreadStateStore.getMentionCount(fav.channelId) || (UnreadStateStore.hasUnread(fav.channelId) ? 1 : 0), | |
unreadEstimated: UnreadStateStore.isEstimated(fav.channelId) || (UnreadStateStore.hasUnread(fav.channelId) && UnreadStateStore.getUnreadCount(fav.channelId) === 0), | |
hasUnread: UnreadStateStore.hasUnread(fav.channelId), | |
mentionCount: UnreadStateStore.getMentionCount(fav.channelId), | |
selected: SelectedChannelStore.getChannelId()===fav.channelId, | |
isTyping: isChannelTyping(fav.channelId), | |
currentStatus: getCurrentUserStatus(fav.url) | |
}; | |
} | |
}; | |
const getCurrentUserStatus = (pathname = location.pathname)=> | |
{ | |
const cId = (pathname.match(/^\/channels\/(\d+|@me|@favorites)\/(\d+)/) || [])[2]; | |
if(cId) | |
{ | |
const channel = ChannelStore.getChannel(cId); | |
if(channel?.guild_id) | |
{ | |
return "none"; | |
} | |
else if(channel?.isDM()) | |
{ | |
const user = UserStore.getUser(channel.getRecipientId()); | |
const status = UserStatusStore.getStatus(user.id); | |
return status; | |
} | |
else if(channel?.isGroupDM()) | |
{ | |
return "none"; | |
} | |
} | |
return "none"; | |
}; | |
const getChannelTypingTooltipText = (userIds) => | |
{ | |
if (userIds) | |
{ | |
const usernames = userIds.map(userId => UserStore.getUser(userId)).filter(user => user).map(user => user.tag); | |
const remainingUserCount = userIds.length - usernames.length; | |
const text = (()=>{ | |
if(usernames.length === 0){ | |
return `${remainingUserCount} user${remainingUserCount > 1 ? "s" : ""}`; | |
}else if(userIds.length > 2){ | |
const otherCount = usernames.length - 1 + remainingUserCount; | |
return `${usernames[0]} and ${otherCount} other${otherCount > 1 ? "s" : ""}`; | |
}else if(remainingUserCount === 0){ | |
return usernames.join(", "); | |
}else{ | |
return `${usernames.join(", ")} and ${remainingUserCount} other${remainingUserCount > 1 ? "s" : ""}`; | |
} | |
})(); | |
return text; | |
} | |
return "Someone is Typing..."; | |
}; | |
const getChannelTypingUsers = (channel_id) => | |
{ | |
const channel = ChannelStore.getChannel(channel_id); | |
const selfId = UserStore.getCurrentUser()?.id; | |
if (channel) | |
{ | |
const userIds = Object.keys(UserTypingStore.getTypingUsers(channel_id)).filter(uId => (uId !== selfId)); | |
const typingUsers = [...new Set(userIds)]; | |
return typingUsers; | |
} | |
return null; | |
}; | |
const isChannelTyping = (channel_id) => | |
{ | |
const channel = ChannelStore.getChannel(channel_id); | |
const selfId = UserStore.getCurrentUser()?.id; | |
if (channel) | |
{ | |
const userIds = Object.keys(UserTypingStore.getTypingUsers(channel_id)).filter(uId => (uId !== selfId)); | |
const typingUsers = [...new Set(userIds)]; | |
if (typingUsers) return typingUsers.length === 0 ? false : true; | |
} | |
return false; | |
}; | |
const isChannelDM = (channel_id) => | |
{ | |
return (()=>{const c=ChannelStore.getChannel(channel_id); return c && (c.isDM()||c.isGroupDM());})() | |
}; | |
const getCurrentName = (pathname = location.pathname)=> | |
{ | |
const cId = (pathname.match(/^\/channels\/(\d+|@me|@favorites)\/(\d+)/) || [])[2]; | |
if(cId){ | |
const channel = ChannelStore.getChannel(cId); | |
if(channel?.name) return (channel.guildId ? "@" : "#") + channel.name; | |
else if(channel?.rawRecipients) return "@" + channel.rawRecipients.map(u=>u.username).join(", "); | |
else return pathname; | |
}else{ | |
if(pathname === "/channels/@me") return "Friends"; | |
else if(pathname.match(/^\/[a-z\-]+$/)) return pathname.substr(1).split("-").map(part => part.substr(0, 1).toUpperCase() + part.substr(1)).join(" "); | |
else return pathname; | |
} | |
}; | |
const getCurrentIconUrl = (pathname = location.pathname)=> | |
{ | |
const cId = (pathname.match(/^\/channels\/(\d+|@me|@favorites)\/(\d+)/) || [])[2]; | |
if(cId){ | |
const channel = ChannelStore.getChannel(cId); | |
if(!channel) return ""; | |
if(channel.guild_id){ | |
const guild = GuildStore.getGuild(channel.guild_id); | |
return guild.getIconURL(40, false) || DefaultUserIconBlue; | |
}else if(channel.isDM()){ | |
const user = UserStore.getUser(channel.getRecipientId()); | |
return user.getAvatarURL(null, 40, false); | |
}else if(channel.isGroupDM()){ | |
if(channel.icon) return `https://cdn.discordapp.com/channel-icons/${channel.id}/${channel.icon}.webp`; | |
else return DefaultUserIconGreen; | |
} | |
} | |
return DefaultUserIconGrey; | |
}; | |
//#endregion | |
//#region Tab Definitions | |
const GetTabStyles = (viewMode, item)=> | |
{ | |
if (item === "unreadBadge") | |
{ | |
if (viewMode === "classic") return " channelTabs-classicBadgeAlignment"; | |
else if (viewMode === "alt") return " channelTabs-badgeAlignLeft"; | |
} | |
else if (item === "mentionBadge") | |
{ | |
if (viewMode === "classic") return " channelTabs-classicBadgeAlignment"; | |
else if (viewMode === "alt") return " channelTabs-badgeAlignRight"; | |
} | |
else if (item === "typingBadge") | |
{ | |
if (viewMode === "classic") return " channelTabs-classicBadgeAlignment"; | |
else if (viewMode === "alt") return " channelTabs-typingBadgeAlignment"; | |
} | |
return ""; | |
}; | |
const TabIcon = props=>React.createElement( | |
"img", | |
{ | |
className: "channelTabs-tabIcon", | |
src: !props.iconUrl ? DefaultUserIconGrey :props.iconUrl | |
} | |
); | |
const TabStatus = props=>React.createElement( | |
"rect", | |
{ | |
width: 6, | |
height: 6, | |
x: 14, | |
y: 14, | |
className: "channelTabs-tabStatus" | |
+ (props.currentStatus == "online" ? " channelTabs-onlineIcon" : "") | |
+ (props.currentStatus == "idle" ? " channelTabs-idleIcon" : "") | |
+ (props.currentStatus == "dnd" ? " channelTabs-doNotDisturbIcon" : "") | |
+ (props.currentStatus == "offline" ? " channelTabs-offlineIcon" : "") | |
+ (props.currentStatus == "none" ? " channelTabs-noneIcon" : "") | |
} | |
); | |
const TabName = props=>React.createElement( | |
"span", | |
{ | |
className: "channelTabs-tabName" | |
}, | |
props.name | |
); | |
const TabClose = props=>props.tabCount < 2 ? null : React.createElement( | |
"div", | |
{ | |
className: "channelTabs-closeTab", | |
onClick: e=>{ | |
e.stopPropagation(); | |
props.closeTab(); | |
} | |
}, | |
React.createElement(Close, {}) | |
); | |
const TabUnreadBadge = props=>React.createElement("div", { | |
className: "channelTabs-unreadBadge" + (!props.hasUnread ? " channelTabs-noUnread" : "") + GetTabStyles(props.viewMode, "unreadBadge") | |
}, props.unreadCount + (props.unreadEstimated ? "+" : "")); | |
const TabMentionBadge = props=>React.createElement("div", { | |
className: "channelTabs-mentionBadge" + (props.mentionCount === 0 ? " channelTabs-noMention" : "") + GetTabStyles(props.viewMode, "mentionBadge") | |
}, props.mentionCount); | |
const TabTypingBadge = ({viewMode, isTyping, userIds})=>{ | |
if (isTyping === false) return null; | |
const text = getChannelTypingTooltipText(userIds); | |
return React.createElement( | |
"div", | |
{ | |
className: "channelTabs-TypingContainer" + GetTabStyles(viewMode, "typingBadge") | |
}, | |
React.createElement( | |
Tooltip, | |
{ | |
text, | |
position: "bottom" | |
}, | |
tooltipProps => React.createElement(Spinner, { | |
...tooltipProps, | |
type: "pulsingEllipsis", | |
className: `channelTabs-typingBadge`, | |
animated: isTyping, | |
style: { | |
opacity: 0.7 | |
} | |
}) | |
) | |
); | |
}; | |
const CozyTab = (props)=>{ | |
return React.createElement( | |
"div", | |
{}, | |
React.createElement("svg", { | |
className: "channelTabs-tabIconWrapper", | |
width: "20", | |
height: "20", | |
viewBox: "0 0 20 20" | |
}, | |
props.currentStatus === "none" | |
? React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20 }, React.createElement(TabIcon, { iconUrl: props.iconUrl })) | |
: React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20, mask: "url(#svg-mask-avatar-status-round-20)" }, React.createElement(TabIcon, { iconUrl: props.iconUrl })), | |
props.currentStatus === "none" ? null : React.createElement(TabStatus, { currentStatus: props.currentStatus }) | |
), | |
React.createElement(TabName, {name: props.name}), | |
React.createElement( | |
"div", | |
{ | |
className: "channelTabs-gridContainer", | |
}, | |
React.createElement( | |
"div", | |
{className: "channelTabs-gridItemBR"}, | |
!(props.selected ? props.showActiveTabTypingBadge : props.showTabTypingBadge) ? null : React.createElement(TabTypingBadge, {viewMode: "alt", isTyping: props.hasUsersTyping, userIds: getChannelTypingUsers(props.channelId)}) | |
), | |
React.createElement( | |
"div", | |
{className: "channelTabs-gridItemTL"}, | |
!(props.selected ? props.showActiveTabUnreadBadges : props.showTabUnreadBadges) ? null : !props.channelId || (ChannelStore.getChannel(props.channelId)?.isPrivate() ?? true) ? null : !(props.selected ? props.showEmptyActiveTabBadges : props.showEmptyTabBadges) && !props.hasUnread ? null : React.createElement(TabUnreadBadge, {viewMode: "alt", unreadCount: props.unreadCount, unreadEstimated: props.unreadEstimated, hasUnread: props.hasUnread, mentionCount: props.mentionCount}) | |
), | |
React.createElement( | |
"div", | |
{className: "channelTabs-gridItemTR"}, | |
!(props.selected ? props.showActiveTabMentionBadges : props.showTabMentionBadges) ? null : !(props.selected ? props.showEmptyActiveTabBadges : props.showEmptyTabBadges) && (props.mentionCount === 0) ? null : React.createElement(TabMentionBadge, {viewMode: "alt", mentionCount: props.mentionCount}) | |
), | |
React.createElement("div", {className: "channelTabs-gridItemBL"})) | |
) | |
}; | |
const CompactTab = (props)=>{ | |
return React.createElement( | |
"div", | |
{}, | |
React.createElement("svg", { | |
className: "channelTabs-tabIconWrapper", | |
width: "20", | |
height: "20", | |
viewBox: "0 0 20 20" | |
}, | |
props.currentStatus === "none" | |
? React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20 }, React.createElement(TabIcon, { iconUrl: props.iconUrl })) | |
: React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20, mask: "url(#svg-mask-avatar-status-round-20)" }, React.createElement(TabIcon, { iconUrl: props.iconUrl })), | |
props.currentStatus === "none" ? null : React.createElement(TabStatus, { currentStatus: props.currentStatus }) | |
), | |
React.createElement(TabName, {name: props.name}), | |
!(props.selected ? props.showActiveTabTypingBadge : props.showTabTypingBadge) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
React.createElement(TabTypingBadge, {viewMode: "classic", isTyping: props.hasUsersTyping, userIds: getChannelTypingUsers(props.channelId)}) | |
), | |
!(props.selected ? props.showActiveTabUnreadBadges : props.showTabUnreadBadges) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
!props.channelId || (ChannelStore.getChannel(props.channelId)?.isPrivate() ?? true) ? null : !(props.selected ? props.showEmptyActiveTabBadges : props.showEmptyTabBadges) && !props.hasUnread ? null : React.createElement(TabUnreadBadge, {viewMode: "classic", unreadCount: props.unreadCount, unreadEstimated: props.unreadEstimated, hasUnread: props.hasUnread, mentionCount: props.mentionCount}) | |
), | |
!(props.selected ? props.showActiveTabMentionBadges : props.showTabMentionBadges) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
!(props.selected ? props.showEmptyActiveTabBadges : props.showEmptyTabBadges) && (props.mentionCount === 0) ? null : React.createElement(TabMentionBadge, {viewMode: "classic", mentionCount: props.mentionCount}) | |
) | |
) | |
}; | |
const Tab = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-tab" | |
+ (props.selected ? " channelTabs-selected" : "") | |
+ (props.minimized ? " channelTabs-minimized" : "") | |
+ (props.hasUnread ? " channelTabs-unread" : "") | |
+ (props.mentionCount > 0 ? " channelTabs-mention" : ""), | |
"data-mention-count": props.mentionCount, | |
"data-unread-count": props.unreadCount, | |
"data-unread-estimated": props.unreadEstimated, | |
onClick: ()=>{if(!props.selected) props.switchToTab(props.tabIndex);}, | |
onMouseUp: e=>{ | |
if(e.button !== 1) return; | |
e.preventDefault(); | |
props.closeTab(props.tabIndex); | |
}, | |
onContextMenu: e=> {CreateTabContextMenu(props,e)}, | |
onMouseOver: e=> { | |
if (currentTabDragIndex == props.tabIndex || currentTabDragIndex == -1) return; | |
currentTabDragDestinationIndex = props.tabIndex; | |
}, | |
onMouseDown: e => { | |
let mouseMove = e2 => { | |
if (Math.sqrt((e.pageX - e2.pageX)**2) > 20 || Math.sqrt((e.pageY - e2.pageY)**2) > 20) { | |
currentTabDragIndex = props.tabIndex; | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
let dragging = e3 => { | |
if (currentTabDragIndex != currentTabDragDestinationIndex) | |
{ | |
if (currentTabDragDestinationIndex != -1) | |
{ | |
props.moveTab(currentTabDragIndex, currentTabDragDestinationIndex); | |
currentTabDragDestinationIndex = currentTabDragDestinationIndex; | |
currentTabDragIndex = currentTabDragDestinationIndex; | |
} | |
} | |
}; | |
let releasing = e3 => { | |
document.removeEventListener("mousemove", dragging); | |
document.removeEventListener("mouseup", releasing); | |
currentTabDragIndex = -1; | |
currentTabDragDestinationIndex = -1; | |
}; | |
document.addEventListener("mousemove", dragging); | |
document.addEventListener("mouseup", releasing); | |
} | |
}; | |
let mouseUp = _ => { | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
}; | |
document.addEventListener("mousemove", mouseMove); | |
document.addEventListener("mouseup", mouseUp); | |
}, | |
}, | |
props.compactStyle ? CompactTab(props) : CozyTab(props), | |
React.createElement(TabClose, {tabCount: props.tabCount, closeTab: ()=>props.closeTab(props.tabIndex)}) | |
); | |
//#endregion | |
//#region Fav Definitions | |
const FavMoveToGroupList = props => { | |
var groups = props.favGroups.map( | |
(group, index) => { | |
var entry = { | |
label: group.name, | |
id: "entry" + index, | |
action: () => props.moveToFavGroup(props.favIndex, group.groupId) | |
}; | |
return entry; | |
} | |
); | |
if (groups.length === 0) { | |
return [{ | |
label: "No groups", | |
disabled: true | |
}] | |
} | |
return groups; | |
} | |
const FavIcon = props=>React.createElement( | |
"img", | |
{ | |
className: "channelTabs-favIcon", | |
src: !props.iconUrl ? DefaultUserIconGrey :props.iconUrl | |
} | |
); | |
const FavStatus = props=>React.createElement( | |
"rect", | |
{ | |
width: 6, | |
height: 6, | |
x: 14, | |
y: 14, | |
className: "channelTabs-favStatus" | |
+ (props.currentStatus == "online" ? " channelTabs-onlineIcon" : "") | |
+ (props.currentStatus == "idle" ? " channelTabs-idleIcon" : "") | |
+ (props.currentStatus == "dnd" ? " channelTabs-doNotDisturbIcon" : "") | |
+ (props.currentStatus == "offline" ? " channelTabs-offlineIcon" : "") | |
+ (props.currentStatus == "none" ? " channelTabs-noneIcon" : "") | |
} | |
); | |
const FavName = props=>React.createElement( | |
"span", | |
{ | |
className: "channelTabs-favName" | |
}, | |
props.name | |
); | |
const FavUnreadBadge = props=>React.createElement("div", { | |
className: "channelTabs-unreadBadge" + (!props.hasUnread ? " channelTabs-noUnread" : "") | |
}, props.unreadCount + (props.unreadEstimated ? "+" : "")); | |
const FavMentionBadge = props=>React.createElement("div", { | |
className: "channelTabs-mentionBadge" + (props.mentionCount === 0 ? " channelTabs-noMention" : "") | |
}, props.mentionCount); | |
const FavTypingBadge = ({isTyping, userIds})=>{ | |
const text = getChannelTypingTooltipText(userIds); | |
return React.createElement( | |
Tooltip, | |
{ | |
text, | |
position: "bottom" | |
}, | |
tooltipProps => React.createElement("div", { | |
...tooltipProps, | |
className: "channelTabs-typingBadge" + (!isTyping ? " channelTabs-noTyping" : "") | |
}, React.createElement(Spinner, { | |
type: "pulsingEllipsis", | |
animated: (!isTyping ? false : true) | |
})) | |
) | |
}; | |
const Fav = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-fav" | |
+ (props.channelId ? " channelTabs-channel" : props.guildId ? " channelTabs-guild" : "") | |
+ (props.selected ? " channelTabs-selected" : "") | |
+ (props.minimized ? " channelTabs-minimized" : "") | |
+ (props.hasUnread ? " channelTabs-unread" : "") | |
+ (props.mentionCount > 0 ? " channelTabs-mention" : ""), | |
"data-mention-count": props.mentionCount, | |
"data-unread-count": props.unreadCount, | |
"data-unread-estimated": props.unreadEstimated, | |
onClick: ()=>props.guildId ? NavigationUtils.transitionToGuild(props.guildId, SelectedChannelStore.getChannelId(props.guildId)) : NavigationUtils.transitionTo(props.url), | |
onMouseUp: e=>{ | |
if(e.button !== 1) return; | |
e.preventDefault(); | |
props.openInNewTab(); | |
}, | |
onContextMenu: e=> {CreateFavContextMenu(props,e)}, | |
onMouseOver: e=> { | |
if (currentFavDragIndex == props.favIndex || currentFavDragIndex == -1) return; | |
currentFavDragDestinationIndex = props.favIndex; | |
}, | |
onMouseDown: e => { | |
let mouseMove = e2 => { | |
if (Math.sqrt((e.pageX - e2.pageX)**2) > 20 || Math.sqrt((e.pageY - e2.pageY)**2) > 20) { | |
currentFavDragIndex = props.favIndex; | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
let dragging = e3 => { | |
if (currentFavDragIndex != currentFavDragDestinationIndex) | |
{ | |
if (currentFavDragDestinationIndex != -1) | |
{ | |
props.moveFav(currentFavDragIndex, currentFavDragDestinationIndex); | |
currentFavDragDestinationIndex = currentFavDragDestinationIndex; | |
currentFavDragIndex = currentFavDragDestinationIndex; | |
} | |
} | |
}; | |
let releasing = e3 => { | |
document.removeEventListener("mousemove", dragging); | |
document.removeEventListener("mouseup", releasing); | |
currentFavDragIndex = -1; | |
currentFavDragDestinationIndex = -1; | |
}; | |
document.addEventListener("mousemove", dragging); | |
document.addEventListener("mouseup", releasing); | |
} | |
}; | |
let mouseUp = _ => { | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
}; | |
document.addEventListener("mousemove", mouseMove); | |
document.addEventListener("mouseup", mouseUp); | |
}, | |
}, | |
React.createElement("svg", { | |
className: "channelTabs-favIconWrapper", | |
width: "20", | |
height: "20", | |
viewBox: "0 0 20 20" | |
}, | |
props.currentStatus === "none" | |
? React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20 }, React.createElement(FavIcon, { iconUrl: props.iconUrl })) | |
: React.createElement("foreignObject", { x: 0, y: 0, width: 20, height: 20, mask: "url(#svg-mask-avatar-status-round-20)" }, React.createElement(FavIcon, { iconUrl: props.iconUrl })), | |
props.currentStatus === "none" ? null : React.createElement(FavStatus, { currentStatus: props.currentStatus }) | |
), | |
React.createElement(FavName, {name: props.name}), | |
!(props.showFavUnreadBadges && (props.channelId || props.guildId)) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
isChannelDM(props.channelId) ? null : !props.showEmptyFavBadges && props.unreadCount === 0 ? null : React.createElement(FavUnreadBadge, {unreadCount: props.unreadCount, unreadEstimated: props.unreadEstimated, hasUnread: props.hasUnread}) | |
), | |
!(props.showFavMentionBadges && (props.channelId || props.guildId)) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
!props.showEmptyFavBadges && props.mentionCount === 0 ? null : React.createElement(FavMentionBadge, {mentionCount: props.mentionCount}) | |
), | |
!(props.showFavTypingBadge && (props.channelId || props.guildId)) ? null : React.createElement( | |
React.Fragment, | |
{}, | |
React.createElement(FavTypingBadge, {isTyping: props.isTyping, userIds: getChannelTypingUsers(props.channelId)}) | |
) | |
); | |
//#endregion | |
//#region Misc. Definitions | |
const NewTab = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-newTab", | |
onClick: props.openNewTab | |
}, | |
React.createElement(PlusAlt, {}) | |
); | |
//#endregion | |
//#region FavItems/FavFolders Definitions | |
const NoFavItemsPlaceholder = props=>React.createElement("span", { | |
className: "channelTabs-noFavNotice" | |
}, "You don't have any favs yet. Right click a tab to mark it as favourite. You can disable this bar in the settings." | |
); | |
const FavItems = props=>{ | |
var isDefault = (props.group === null); | |
return props.favs.filter(item => item).map( | |
(fav, favIndex) => | |
{ | |
var canCreate = (isDefault ? fav.groupId === -1 : fav.groupId === props.group.groupId); | |
return canCreate ? React.createElement( | |
Flux.connectStores([UnreadStateStore, UserTypingStore, SelectedChannelStore], ()=> updateFavEntry(fav)) | |
( | |
result => React.createElement( | |
Fav, | |
{ | |
name: fav.name, | |
iconUrl: fav.iconUrl, | |
url: fav.url, | |
favCount: props.favs.length, | |
favGroups: props.favGroups, | |
rename: ()=>props.rename(fav.name, favIndex), | |
delete: ()=>props.delete(favIndex), | |
openInNewTab: ()=>props.openInNewTab(fav), | |
moveLeft: ()=>props.move(favIndex, (favIndex + props.favs.length - 1) % props.favs.length), | |
moveRight: ()=>props.move(favIndex, (favIndex + 1) % props.favs.length), | |
minimizeFav: props.minimizeFav, | |
minimized: fav.minimized, | |
moveToFavGroup: props.moveToFavGroup, | |
moveFav: props.move, | |
favIndex, | |
channelId: fav.channelId, | |
guildId: fav.guildId, | |
groupId: fav.groupId, | |
showFavUnreadBadges: props.showFavUnreadBadges, | |
showFavMentionBadges: props.showFavMentionBadges, | |
showFavTypingBadge: props.showFavTypingBadge, | |
showEmptyFavBadges: props.showEmptyFavBadges, | |
isTyping: isChannelTyping(fav.channelId), | |
currentStatus: getCurrentUserStatus(fav.url), | |
...result | |
} | |
) | |
) | |
) : null; | |
} | |
); | |
}; | |
const FavFolder = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-favGroup", | |
onContextMenu: e=>{CreateFavGroupContextMenu(props,e)}, | |
onMouseOver: e=> { | |
if (currentGroupDragIndex == props.groupIndex || currentGroupDragIndex == -1) return; | |
currentGroupDragDestinationIndex = props.groupIndex; | |
}, | |
onMouseDown: e => { | |
let mouseMove = e2 => { | |
if (Math.sqrt((e.pageX - e2.pageX)**2) > 20 || Math.sqrt((e.pageY - e2.pageY)**2) > 20) { | |
currentGroupDragIndex = props.groupIndex; | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
let dragging = e3 => { | |
if (currentGroupDragIndex != currentGroupDragDestinationIndex) | |
{ | |
if (currentGroupDragDestinationIndex != -1) | |
{ | |
props.moveFavGroup(currentGroupDragIndex, currentGroupDragDestinationIndex); | |
currentGroupDragDestinationIndex = currentGroupDragDestinationIndex; | |
currentGroupDragIndex = currentGroupDragDestinationIndex; | |
} | |
} | |
}; | |
let releasing = e3 => { | |
document.removeEventListener("mousemove", dragging); | |
document.removeEventListener("mouseup", releasing); | |
currentGroupDragIndex = -1; | |
currentGroupDragDestinationIndex = -1; | |
}; | |
document.addEventListener("mousemove", dragging); | |
document.addEventListener("mouseup", releasing); | |
} | |
}; | |
let mouseUp = _ => { | |
document.removeEventListener("mousemove", mouseMove); | |
document.removeEventListener("mouseup", mouseUp); | |
}; | |
document.addEventListener("mousemove", mouseMove); | |
document.addEventListener("mouseup", mouseUp); | |
} | |
}, | |
React.createElement( | |
"div", | |
{ | |
className: "channelTabs-favGroupBtn", | |
onClick: () => { | |
closeAllDropdowns(); | |
document.getElementById("favGroup-content-" + props.groupIndex).classList.toggle("channelTabs-favGroupShow"); | |
currentGroupOpened = props.groupIndex; | |
} | |
}, | |
props.favGroup.name, | |
props.showFavGroupMentionBadges ? props.mentionCountGroup == 0 && !props.showEmptyFavGroupBadges ? null : React.createElement(FavMentionBadge, {mentionCount: props.mentionCountGroup}) : null, | |
props.showFavGroupUnreadBadges ? props.unreadCountGroup == 0 && !props.showEmptyFavGroupBadges ? null : React.createElement(FavUnreadBadge, {unreadCount: props.unreadCountGroup, unreadEstimated: props.unreadEstimatedGroup, hasUnread: props.hasUnreadGroup}) : null, | |
props.showFavGroupTypingBadge && (props.isTypingGroup) ? React.createElement(FavTypingBadge, {isTyping: props.isTypingGroup, userIds: null}) : null | |
), | |
React.createElement( | |
"div", | |
{ | |
className: "channelTabs-favGroup-content" + (currentGroupOpened === props.groupIndex ? " channelTabs-favGroupShow" : ""), | |
id: "favGroup-content-" + props.groupIndex | |
}, | |
React.createElement(FavItems, {group: props.favGroup, ...props}) | |
) | |
); | |
const FavFolders = (props)=>{ | |
return props.favGroups.map((favGroup, index) => | |
{ | |
return React.createElement(Flux.connectStores([UnreadStateStore, SelectedChannelStore, UserTypingStore], () => | |
{ | |
var unreadCount = 0; | |
var unreadEstimated = 0; | |
var hasUnread = false; | |
var mentionCount = 0; | |
var isTyping = false; | |
props.favs.filter(item => item).forEach((fav, favIndex) => | |
{ | |
var canCreate = fav.groupId === favGroup.groupId; | |
if (canCreate) | |
{ | |
var hasUnreads = isChannelDM(fav.channelId); | |
var result = updateFavEntry(fav); | |
if (!hasUnreads) unreadCount += result.unreadCount; | |
mentionCount += result.mentionCount; | |
if (!hasUnreads) unreadEstimated += result.unreadEstimated; | |
if (!hasUnreads) hasUnread = (result.hasUnread ? true : hasUnread); | |
isTyping = (result.isTyping ? true : isTyping); | |
} | |
} | |
); | |
return { | |
unreadCount, | |
mentionCount, | |
unreadEstimated, | |
mentionCount, | |
hasUnread, | |
isTyping | |
}; | |
}) | |
( | |
result => | |
{ | |
return React.createElement(FavFolder, | |
{ | |
groupIndex: index, | |
groupCount: props.favGroups.length, | |
favGroup: favGroup, | |
unreadCountGroup: result.unreadCount, | |
unreadEstimatedGroup: result.unreadEstimated, | |
mentionCountGroup: result.mentionCount, | |
hasUnreadGroup: result.hasUnread, | |
isTypingGroup: result.isTyping, | |
showFavGroupUnreadBadges: props.showFavGroupUnreadBadges, | |
showFavGroupMentionBadges: props.showFavGroupMentionBadges, | |
showFavGroupTypingBadge: props.showFavGroupTypingBadge, | |
showEmptyFavGroupBadges: props.showEmptyFavGroupBadges, | |
...props | |
}); | |
} | |
)); | |
} | |
); | |
}; | |
//#endregion | |
//#region FavBar/TopBar/TabBar Definitions | |
function nextTab(){ | |
if(TopBarRef.current) TopBarRef.current.switchToTab((TopBarRef.current.state.selectedTabIndex + 1) % TopBarRef.current.state.tabs.length); | |
} | |
function previousTab(){ | |
if(TopBarRef.current) TopBarRef.current.switchToTab((TopBarRef.current.state.selectedTabIndex - 1 + TopBarRef.current.state.tabs.length) % TopBarRef.current.state.tabs.length); | |
} | |
function closeCurrentTab(){ | |
if(TopBarRef.current) TopBarRef.current.closeTab(TopBarRef.current.state.selectedTabIndex); | |
} | |
const TabBar = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-tabContainer", | |
"data-tab-count": props.tabs.length | |
}, | |
React.createElement("div", { | |
className: "channelTabs-tabNav" | |
}, | |
React.createElement("div", { | |
className: "channelTabs-tabNavLeft", | |
onClick: () =>{ TopBarRef.current.state.useStandardNav ? NavShortcuts.NAVIGATE_BACK.action() : previousTab(); }, | |
onContextMenu: () =>{ !TopBarRef.current.state.useStandardNav ? NavShortcuts.NAVIGATE_BACK.action() : previousTab(); } | |
}, | |
React.createElement(LeftCaret, {})), | |
React.createElement("div", { | |
className: "channelTabs-tabNavRight", | |
onClick: () =>{ TopBarRef.current.state.useStandardNav ? NavShortcuts.NAVIGATE_FORWARD.action() : nextTab(); }, | |
onContextMenu: () =>{ !TopBarRef.current.state.useStandardNav ? NavShortcuts.NAVIGATE_FORWARD.action() : nextTab(); } | |
}, | |
React.createElement(RightCaret, {})), | |
React.createElement("div", { | |
className: "channelTabs-tabNavClose", | |
onClick: () =>{ closeCurrentTab() }, | |
onContextMenu: props.openNewTab | |
}, | |
React.createElement(Close, {})) | |
), | |
props.tabs.map((tab, tabIndex)=>React.createElement(Flux.connectStores([UnreadStateStore, UserTypingStore, UserStatusStore], ()=>({ | |
unreadCount: UnreadStateStore.getUnreadCount(tab.channelId), | |
unreadEstimated: UnreadStateStore.isEstimated(tab.channelId), | |
hasUnread: UnreadStateStore.hasUnread(tab.channelId), | |
mentionCount: UnreadStateStore.getMentionCount(tab.channelId), | |
hasUsersTyping: isChannelTyping(tab.channelId), | |
currentStatus: getCurrentUserStatus(tab.url) | |
}))(result => React.createElement( | |
Tab, | |
{ | |
switchToTab: props.switchToTab, | |
closeTab: props.closeTab, | |
addToFavs: props.addToFavs, | |
minimizeTab: props.minimizeTab, | |
moveLeft: ()=>props.move(tabIndex, (tabIndex + props.tabs.length - 1) % props.tabs.length), | |
moveRight: ()=>props.move(tabIndex, (tabIndex + 1) % props.tabs.length), | |
openInNewTab: ()=>props.openInNewTab(tab), | |
moveTab: props.move, | |
tabCount: props.tabs.length, | |
tabIndex, | |
name: tab.name, | |
iconUrl: tab.iconUrl, | |
currentStatus: result.currentStatus, | |
url: tab.url, | |
selected: tab.selected, | |
minimized: tab.minimized, | |
channelId: tab.channelId, | |
unreadCount: result.unreadCount, | |
unreadEstimated: result.unreadEstimated, | |
hasUnread: result.hasUnread, | |
mentionCount: result.mentionCount, | |
hasUsersTyping: result.hasUsersTyping, | |
showTabUnreadBadges: props.showTabUnreadBadges, | |
showTabMentionBadges: props.showTabMentionBadges, | |
showTabTypingBadge: props.showTabTypingBadge, | |
showEmptyTabBadges: props.showEmptyTabBadges, | |
showActiveTabUnreadBadges: props.showActiveTabUnreadBadges, | |
showActiveTabMentionBadges: props.showActiveTabMentionBadges, | |
showActiveTabTypingBadge: props.showActiveTabTypingBadge, | |
showEmptyActiveTabBadges: props.showEmptyActiveTabBadges, | |
compactStyle: props.compactStyle | |
} | |
)))), | |
React.createElement(NewTab, { | |
openNewTab: props.openNewTab | |
}) | |
); | |
const FavBar = props=>React.createElement( | |
"div", | |
{ | |
className: "channelTabs-favContainer" + (props.favs.length == 0 ? " channelTabs-noFavs" : ""), | |
"data-fav-count": props.favs.length, | |
onContextMenu: e=>{CreateFavBarContextMenu(props, e);} | |
}, | |
React.createElement(FavFolders, props), | |
props.favs.length > 0 ? React.createElement(FavItems, {group: null, ...props}) : React.createElement(NoFavItemsPlaceholder, {}), | |
); | |
const TopBar = class TopBar extends React.Component { | |
//#region Constructor | |
constructor(props){ | |
super(props); | |
this.state = { | |
selectedTabIndex: Math.max(props.tabs.findIndex(tab => tab.selected), 0), | |
tabs: props.tabs, | |
favs: props.favs, | |
favGroups: props.favGroups, | |
showTabBar: props.showTabBar, | |
showFavBar: props.showFavBar, | |
showFavUnreadBadges: props.showFavUnreadBadges, | |
showFavMentionBadges: props.showFavMentionBadges, | |
showFavTypingBadge: props.showFavTypingBadge, | |
showEmptyFavBadges: props.showEmptyFavBadges, | |
showTabUnreadBadges: props.showTabUnreadBadges, | |
showTabMentionBadges: props.showTabMentionBadges, | |
showTabTypingBadge: props.showTabTypingBadge, | |
showEmptyTabBadges: props.showEmptyTabBadges, | |
showActiveTabUnreadBadges: props.showActiveTabUnreadBadges, | |
showActiveTabMentionBadges: props.showActiveTabMentionBadges, | |
showActiveTabTypingBadge: props.showActiveTabTypingBadge, | |
showEmptyActiveTabBadges: props.showEmptyActiveTabBadges, | |
showFavGroupUnreadBadges: props.showFavGroupUnreadBadges, | |
showFavGroupMentionBadges: props.showFavGroupMentionBadges, | |
showFavGroupTypingBadge: props.showFavGroupTypingBadge, | |
showEmptyFavGroupBadges: props.showEmptyFavGroupBadges, | |
addFavGroup: props.addFavGroup, | |
compactStyle: props.compactStyle, | |
showQuickSettings: props.showQuickSettings, | |
showNavButtons: props.showNavButtons, | |
alwaysFocusNewTabs: props.alwaysFocusNewTabs, | |
useStandardNav: props.useStandardNav | |
}; | |
this.switchToTab = this.switchToTab.bind(this); | |
this.closeTab = this.closeTab.bind(this); | |
this.saveChannel = this.saveChannel.bind(this); | |
this.renameFav = this.renameFav.bind(this); | |
this.deleteFav = this.deleteFav.bind(this); | |
this.addToFavs = this.addToFavs.bind(this); | |
this.minimizeTab = this.minimizeTab.bind(this); | |
this.minimizeFav = this.minimizeFav.bind(this); | |
this.moveTab = this.moveTab.bind(this); | |
this.moveFav = this.moveFav.bind(this); | |
this.addFavGroup = this.addFavGroup.bind(this); | |
this.moveToFavGroup = this.moveToFavGroup.bind(this); | |
this.renameFavGroup = this.renameFavGroup.bind(this); | |
this.removeFavGroup = this.removeFavGroup.bind(this); | |
this.moveFavGroup = this.moveFavGroup.bind(this); | |
this.openNewTab = this.openNewTab.bind(this); | |
this.openTabInNewTab = this.openTabInNewTab.bind(this); | |
this.openFavInNewTab = this.openFavInNewTab.bind(this); | |
this.openFavGroupInNewTab = this.openFavGroupInNewTab.bind(this); | |
this.hideFavBar = this.hideFavBar.bind(this); | |
} | |
//#endregion | |
//#region Tab Functions | |
minimizeTab(tabIndex){ | |
this.setState({ | |
tabs: this.state.tabs.map((tab, index) => { | |
if(index == tabIndex) return Object.assign({}, tab, {minimized: !tab.minimized}); | |
else return Object.assign({}, tab); // or return tab; | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
switchToTab(tabIndex){ | |
this.setState({ | |
tabs: this.state.tabs.map((tab, index) => { | |
if(index === tabIndex){ | |
return Object.assign({}, tab, {selected: true}); | |
}else{ | |
return Object.assign({}, tab, {selected: false}); | |
} | |
}), | |
selectedTabIndex: tabIndex | |
}, this.props.plugin.saveSettings); | |
switching = true; | |
NavigationUtils.transitionTo(this.state.tabs[tabIndex].url); | |
switching = false; | |
} | |
closeTab(tabIndex, mode){ | |
if(this.state.tabs.length === 1) return; | |
if (mode === "single" || mode == null) | |
{ | |
this.setState({ | |
tabs: this.state.tabs.filter((tab, index)=>index !== tabIndex), | |
selectedTabIndex: Math.max(0, this.state.selectedTabIndex - (this.state.selectedTabIndex >= tabIndex ? 1 : 0)) | |
}, ()=>{ | |
if(!this.state.tabs[this.state.selectedTabIndex].selected){ | |
this.switchToTab(this.state.selectedTabIndex); | |
} | |
this.props.plugin.saveSettings(); | |
}); | |
} | |
else if (mode == "other") | |
{ | |
this.setState({ | |
tabs: this.state.tabs.filter((tab, index)=>index === tabIndex), | |
selectedTabIndex: 0 | |
}, ()=>{ | |
if(!this.state.tabs[0].selected){ | |
this.switchToTab(this.state.selectedTabIndex); | |
} | |
this.props.plugin.saveSettings(); | |
}); | |
} | |
else if (mode === "left") | |
{ | |
this.setState({ | |
tabs: this.state.tabs.filter((tab, index)=>index >= tabIndex), | |
selectedTabIndex: 0 | |
}, ()=>{ | |
if(!this.state.tabs[this.state.selectedTabIndex].selected){ | |
this.switchToTab(this.state.selectedTabIndex); | |
} | |
this.props.plugin.saveSettings(); | |
}); | |
} | |
else if (mode === "right") | |
{ | |
this.setState({ | |
tabs: this.state.tabs.filter((tab, index)=>index <= tabIndex), | |
selectedTabIndex: tabIndex | |
}, ()=>{ | |
if(!this.state.tabs[this.state.selectedTabIndex].selected){ | |
this.switchToTab(this.state.selectedTabIndex); | |
} | |
this.props.plugin.saveSettings(); | |
}); | |
} | |
} | |
moveTab(fromIndex, toIndex){ | |
if(fromIndex === toIndex) return; | |
const tabs = this.state.tabs.filter((tab, index)=>index !== fromIndex); | |
tabs.splice(toIndex, 0, this.state.tabs[fromIndex]); | |
this.setState({ | |
tabs, | |
selectedTabIndex: tabs.findIndex(tab=>tab.selected) | |
}, this.props.plugin.saveSettings); | |
} | |
//#endregion | |
//#region Fav Functions | |
hideFavBar(){ | |
this.setState({ | |
showFavBar: false | |
}, ()=>{ | |
this.props.plugin.settings.showFavBar = false; | |
this.props.plugin.saveSettings(); | |
}); | |
} | |
renameFav(currentName, favIndex){ | |
let name = currentName; | |
BdApi.showConfirmationModal( | |
"What should the new name be?", | |
React.createElement(Textbox, { | |
onChange: newContent=>name = newContent.trim() | |
}), | |
{ | |
onConfirm: ()=>{ | |
if(!name) return; | |
this.setState({ | |
favs: this.state.favs.map((fav, index)=>{ | |
if(index === favIndex) return Object.assign({}, fav, {name}); | |
else return Object.assign({}, fav); | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
} | |
); | |
} | |
minimizeFav(favIndex){ | |
this.setState({ | |
favs: this.state.favs.map((fav, index) => { | |
if(index == favIndex) return Object.assign({}, fav, {minimized: !fav.minimized}); | |
else return Object.assign({}, fav); // or return tab; | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
deleteFav(favIndex){ | |
this.setState({ | |
favs: this.state.favs.filter((fav, index)=>index!==favIndex) | |
}, this.props.plugin.saveSettings); | |
} | |
/** | |
* The guildId parameter is only passed when the guild is saved and not the channel alone. | |
* This indicates that the currently selected channel needs to get selected instead of the | |
* provided channel id (which should be empty when a guildId is provided) | |
*/ | |
addToFavs(name, iconUrl, url, channelId, guildId){ | |
var groupId = -1; | |
this.setState({ | |
favs: [...this.state.favs, {name, iconUrl, url, channelId, guildId, groupId}] | |
}, this.props.plugin.saveSettings); | |
} | |
moveFav(fromIndex, toIndex){ | |
if(fromIndex === toIndex) return; | |
const favs = this.state.favs.filter((fav, index)=>index !== fromIndex); | |
favs.splice(toIndex, 0, this.state.favs[fromIndex]); | |
this.setState({favs}, this.props.plugin.saveSettings); | |
} | |
//#endregion | |
//#region Fav Group Functions | |
createFavGroupId() | |
{ | |
var generatedId = this.state.favGroups.length; | |
var isUnique = false; | |
var duplicateFound = false; | |
while (!isUnique) | |
{ | |
for (var i = 0; i < this.state.favGroups.length; i++) | |
{ | |
var group = this.state.favGroups[i]; | |
if (generatedId === group.groupId) duplicateFound = true; | |
} | |
if (!duplicateFound) isUnique = true; | |
else | |
{ | |
generatedId++; | |
duplicateFound = false; | |
} | |
} | |
return generatedId; | |
} | |
addFavGroup() | |
{ | |
let name = "New Group"; | |
BdApi.showConfirmationModal( | |
"What should the new name be?", | |
React.createElement(Textbox, { | |
onChange: newContent=>name = newContent.trim() | |
}), | |
{ | |
onConfirm: ()=>{ | |
if(!name) return; | |
this.setState({ | |
favGroups: [...this.state.favGroups, {name: name, groupId: this.createFavGroupId()}] | |
}, this.props.plugin.saveSettings); | |
} | |
} | |
); | |
} | |
renameFavGroup(currentName, groupId) | |
{ | |
let name = currentName; | |
BdApi.showConfirmationModal( | |
"What should the new name be?", | |
React.createElement(Textbox, { | |
onChange: newContent=>name = newContent.trim() | |
}), | |
{ | |
onConfirm: ()=>{ | |
if(!name) return; | |
this.setState({ | |
favGroups: this.state.favGroups.map((group, index)=>{ | |
if(group.groupId === groupId) return Object.assign({}, group, {name}); | |
else return Object.assign({}, group); | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
} | |
); | |
} | |
removeFavGroup(groupId) | |
{ | |
this.setState({ | |
favGroups: this.state.favGroups.filter((group, index)=>group.groupId!==groupId) | |
}, this.props.plugin.saveSettings); | |
this.setState({ | |
favs: this.state.favs.map((fav, index)=>{ | |
if(fav.groupId === groupId) return Object.assign({}, fav, {groupId: -1}); | |
else return Object.assign({}, fav); | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
moveToFavGroup(favIndex, groupId) | |
{ | |
this.setState({ | |
favs: this.state.favs.map((fav, index)=>{ | |
if (index === favIndex) | |
{ | |
return Object.assign({}, fav, {groupId: groupId}); | |
} | |
else | |
{ | |
return Object.assign({}, fav); | |
} | |
}) | |
}, this.props.plugin.saveSettings); | |
} | |
moveFavGroup(fromIndex, toIndex){ | |
if(fromIndex === toIndex) return; | |
const favGroups = this.state.favGroups.filter((group, index)=>index !== fromIndex); | |
favGroups.splice(toIndex, 0, this.state.favGroups[fromIndex]); | |
this.setState({favGroups: favGroups}, this.props.plugin.saveSettings); | |
} | |
//#endregion | |
//#region New Tab Functions | |
saveChannel(guildId, channelId, name, iconUrl) | |
{ | |
if (this.state.alwaysFocusNewTabs) | |
{ | |
//Open and Focus New Tab | |
const newTabIndex = this.state.tabs.length; | |
this.setState({ | |
tabs: [...this.state.tabs.map(tab=>Object.assign(tab, {selected: false})), { | |
url: `/channels/${guildId || "@me"}/${channelId}`, | |
name, | |
iconUrl, | |
channelId, | |
minimized: false, | |
groupId: -1 | |
}], | |
selectedTabIndex: newTabIndex | |
}, ()=>{ | |
this.props.plugin.saveSettings(); | |
this.switchToTab(newTabIndex); | |
}); | |
} | |
else | |
{ | |
//Open New Tab | |
this.setState({ | |
tabs: [...this.state.tabs, { | |
url: `/channels/${guildId || "@me"}/${channelId}`, | |
name, | |
iconUrl, | |
channelId, | |
minimized: false, | |
groupId: -1 | |
}] | |
}, this.props.plugin.saveSettings); | |
} | |
} | |
openNewTab() { | |
const newTabIndex = this.state.tabs.length; | |
this.setState({ | |
tabs: [...this.state.tabs.map(tab=>Object.assign(tab, {selected: false})), { | |
url: "/channels/@me", | |
name: "Friends", | |
selected: true, | |
channelId: undefined | |
}], | |
selectedTabIndex: newTabIndex | |
}, ()=>{ | |
this.props.plugin.saveSettings(); | |
this.switchToTab(newTabIndex); | |
}); | |
} | |
openTabInNewTab(tab) | |
{ | |
//Used to Duplicate Tabs | |
this.setState({ | |
tabs: [...this.state.tabs, Object.assign({}, tab, {selected: false})] | |
}, this.props.plugin.saveSettings); | |
} | |
openFavInNewTab(fav, isGroup) | |
{ | |
if (this.state.alwaysFocusNewTabs && !isGroup) | |
{ | |
//Opens and Focuses New Tab | |
const newTabIndex = this.state.tabs.length; | |
const url = fav.url + (fav.guildId ? `/${fav.guildId}` : ""); | |
this.setState({ | |
tabs: [...this.state.tabs.map(tab=>Object.assign(tab, {selected: false})), { | |
url, | |
name: getCurrentName(url), | |
iconUrl: getCurrentIconUrl(url), | |
currentStatus: getCurrentUserStatus(url), | |
channelId: fav.channelId || SelectedChannelStore.getChannelId(fav.guildId) | |
}], | |
selectedTabIndex: newTabIndex | |
}, ()=>{ | |
this.props.plugin.saveSettings(); | |
this.switchToTab(newTabIndex); | |
}); | |
} | |
else | |
{ | |
//Opens New Tab | |
const url = fav.url + (fav.guildId ? `/${fav.guildId}` : ""); | |
this.setState({ | |
tabs: [...this.state.tabs, { | |
url, | |
selected: false, | |
name: getCurrentName(url), | |
iconUrl: getCurrentIconUrl(url), | |
currentStatus: getCurrentUserStatus(url), | |
channelId: fav.channelId || SelectedChannelStore.getChannelId(fav.guildId) | |
}] | |
}, this.props.plugin.saveSettings); | |
} | |
} | |
openFavGroupInNewTab(groupId) | |
{ | |
this.state.favs.filter(item => item).map( | |
(fav, favIndex) => | |
{ | |
var canCreate = (fav.groupId === groupId); | |
if (canCreate) | |
{ | |
this.openFavInNewTab(fav, true); | |
} | |
} | |
) | |
} | |
//#endregion | |
//#region Other Functions | |
render(){ | |
return React.createElement( | |
"div", | |
{ | |
id: "channelTabs-container" | |
}, | |
!this.state.showQuickSettings ? null : React.createElement('div', | |
{ | |
id: "channelTabs-settingsMenu", | |
dangerouslySetInnerHTML: { __html: SettingsMenuIcon }, | |
onClick: e=>{CreateSettingsContextMenu(this,e);} | |
}), | |
!this.state.showTabBar ? null : React.createElement(TabBar, { | |
tabs: this.state.tabs, | |
showTabUnreadBadges: this.state.showTabUnreadBadges, | |
showTabMentionBadges: this.state.showTabMentionBadges, | |
showTabTypingBadge: this.state.showTabTypingBadge, | |
showEmptyTabBadges: this.state.showEmptyTabBadges, | |
showActiveTabUnreadBadges: this.state.showActiveTabUnreadBadges, | |
showActiveTabMentionBadges: this.state.showActiveTabMentionBadges, | |
showActiveTabTypingBadge: this.state.showActiveTabTypingBadge, | |
showEmptyActiveTabBadges: this.state.showEmptyActiveTabBadges, | |
compactStyle: this.state.compactStyle, | |
privacyMode: this.state.privacyMode, | |
radialStatusMode: this.state.radialStatusMode, | |
tabWidthMin: this.state.tabWidthMin, | |
closeTab: this.closeTab, | |
switchToTab: this.switchToTab, | |
openNewTab: this.openNewTab, | |
openInNewTab: this.openTabInNewTab, | |
addToFavs: this.addToFavs, | |
minimizeTab: this.minimizeTab, | |
move: this.moveTab | |
}), | |
!this.state.showFavBar ? null : React.createElement(FavBar, { | |
favs: this.state.favs, | |
favGroups: this.state.favGroups, | |
showFavUnreadBadges: this.state.showFavUnreadBadges, | |
showFavMentionBadges: this.state.showFavMentionBadges, | |
showFavTypingBadge: this.state.showFavTypingBadge, | |
showEmptyFavBadges: this.state.showEmptyFavBadges, | |
privacyMode: this.state.privacyMode, | |
radialStatusMode: this.state.radialStatusMode, | |
showFavGroupUnreadBadges: this.state.showFavGroupUnreadBadges, | |
showFavGroupMentionBadges: this.state.showFavGroupMentionBadges, | |
showFavGroupTypingBadge: this.state.showFavGroupTypingBadge, | |
showEmptyFavGroupBadges: this.state.showEmptyFavGroupBadges, | |
rename: this.renameFav, | |
delete: this.deleteFav, | |
addToFavs: this.addToFavs, | |
minimizeFav: this.minimizeFav, | |
openInNewTab: this.openFavInNewTab, | |
move: this.moveFav, | |
moveFavGroup: this.moveFavGroup, | |
addFavGroup: this.addFavGroup, | |
moveToFavGroup: this.moveToFavGroup, | |
removeFavGroup: this.removeFavGroup, | |
renameFavGroup: this.renameFavGroup, | |
openFavGroupInNewTab: this.openFavGroupInNewTab, | |
hideFavBar: this.hideFavBar | |
}) | |
); | |
} | |
//#endregion | |
}; | |
const TopBarRef = React.createRef(); | |
//#endregion | |
//#region Plugin Decleration | |
return class ChannelTabs extends Plugin | |
{ | |
//#region Start/Stop Functions | |
constructor(){ | |
super(); | |
} | |
onStart(isRetry = false){ | |
//console.warn("CT Start"); | |
if(isRetry && !BdApi.Plugins.isEnabled(config.info.name)) return; | |
if(!UserStore.getCurrentUser()) return setTimeout(()=>this.onStart(true), 1000); | |
//console.warn(UserStore.getCurrentUser()); | |
patches = []; | |
this.loadSettings(); | |
this.applyStyle(); | |
this.ifNoTabsExist(); | |
this.promises = {state:{cancelled: false}, cancel(){this.state.cancelled = true;}}; | |
this.saveSettings = this.saveSettings.bind(this); | |
this.keybindHandler = this.keybindHandler.bind(this); | |
this.onSwitch(); | |
this.patchAppView(this.promises.state); | |
this.patchContextMenus(); | |
this.ifReopenLastChannelDefault(); | |
document.addEventListener("keydown", this.keybindHandler); | |
window.onclick = (event) => this.clickHandler(event); | |
} | |
onStop(){ | |
this.removeStyle(); | |
document.removeEventListener("keydown", this.keybindHandler); | |
window.onclick = null; | |
Patcher.unpatchAll(); | |
this.promises.cancel(); | |
patches.forEach(patch=>patch()); | |
} | |
//#endregion | |
//#region Styles | |
applyStyle() | |
{ | |
const CompactVariables = ` | |
:root { | |
--channelTabs-tabHeight: 22px; | |
--channelTabs-favHeight: 22px; | |
--channelTabs-tabNameFontSize: 12px; | |
--channelTabs-openTabSize: 18px; | |
} | |
`; | |
const CozyVariables = ` | |
:root { | |
--channelTabs-tabHeight: 32px; | |
--channelTabs-favHeight: 28px; | |
--channelTabs-tabNameFontSize: 13px; | |
--channelTabs-openTabSize: 24px; | |
} | |
`; | |
const ConstantVariables = ` | |
:root { | |
--channelTabs-tabWidth: 220px; | |
--channelTabs-tabWidthMin: ${this.settings.tabWidthMin}px; | |
} | |
`; | |
const PrivacyStyle = ` | |
#app-mount .channelTabs-favGroupBtn { | |
color: transparent !important; | |
} | |
#app-mount .channelTabs-tabName { | |
color: transparent; | |
background-color: var(--interactive-normal); | |
opacity: 0.5; | |
} | |
#app-mount .channelTabs-selected .channelTabs-tabName { | |
background-color: var(--interactive-active); | |
} | |
#app-mount .channelTabs-favName { | |
color: transparent; | |
background-color: var(--interactive-normal); | |
opacity: 0.5; | |
} | |
`; | |
const RadialStatusStyle = ` | |
.channelTabs-tabIconWrapper, | |
.channelTabs-favIconWrapper { | |
overflow: visible; | |
} | |
.channelTabs-tabIconWrapper img[src*="com/avatars/"], | |
.channelTabs-favIconWrapper img[src*="com/avatars/"] { | |
-webkit-clip-path: inset(1px round 50%); | |
clip-path: inset(2px round 50%); | |
} | |
.channelTabs-tabIconWrapper rect, | |
.channelTabs-favIconWrapper rect { | |
x: 0; | |
y: 0; | |
rx: 50%; | |
ry: 50%; | |
-webkit-mask: none; | |
mask: none; | |
fill: none; | |
height: 20px; | |
width: 20px; | |
stroke-width: 2px; | |
} | |
.channelTabs-onlineIcon { | |
stroke: hsl(139, calc(var(--saturation-factor, 1) * 47.3%), 43.9%); | |
} | |
.channelTabs-idleIcon { | |
stroke: hsl(38, calc(var(--saturation-factor, 1) * 95.7%), 54.1%); | |
} | |
.channelTabs-doNotDisturbIcon { | |
stroke: hsl(359, calc(var(--saturation-factor, 1) * 82.6%), 59.4%); | |
} | |
.channelTabs-offlineIcon { | |
stroke: hsl(214, calc(var(--saturation-factor, 1) * 9.9%), 50.4%); | |
} | |
`; | |
const tabNavStyle = ` | |
.channelTabs-tabContainer .channelTabs-tabNav { | |
display:flex; | |
margin: 0 6px 3px 0; | |
} | |
.channelTabs-tabNavClose svg { | |
transform: scale(0.75); | |
} | |
.channelTabs-tabNavLeft svg, | |
.channelTabs-tabNavRight svg { | |
transform: scale(0.6); | |
} | |
/* if clickable */ | |
.channelTabs-tabContainer .channelTabs-tabNav>div:hover { | |
color: var(--interactive-hover); | |
background-color: var(--background-modifier-hover); | |
} | |
.channelTabs-tabContainer .channelTabs-tabNav>div:active { | |
color: var(--interactive-active); | |
background-color: var(--background-modifier-active); | |
} | |
/* if only 1 tab */ | |
.channelTabs-tabContainer[data-tab-count="1"] .channelTabs-tabNav>.channelTabs-tabNavClose { | |
color: var(--interactive-muted); | |
background: none; | |
} | |
.channelTabs-tabNav>div { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
height: var(--channelTabs-tabHeight); | |
width: 32px; | |
border-radius: 4px; | |
margin-right: 3px; | |
color: var(--interactive-normal); | |
} | |
`; | |
const BaseStyle = ` | |
/* | |
//#region Tab Base/Container | |
*/ | |
.channelTabs-tabNav { | |
display:none; | |
} | |
/* | |
//#macos | |
*/ | |
.platform-osx .typeMacOS-3V4xXE { | |
position: relative; | |
width: 100%; | |
-webkit-app-region: drag; | |
} | |
.platform-osx .typeMacOS-3V4xXE>*, | |
.platform-osx .menu-1QACrS { | |
-webkit-app-region: no-drag; | |
} | |
.platform-osx .wrapper-1_HaEi { | |
margin-top: 0; | |
padding-top: 0; | |
} | |
html:not(.platform-win) .sidebar-1tnWFu { | |
border-radius: 8px 0 0; | |
overflow: hidden; | |
} | |
/* | |
//#endregion | |
*/ | |
#channelTabs-container { | |
z-index: 1000; | |
padding: 4px 8px 1px 8px; | |
background: none; | |
} | |
.channelTabs-tabContainer { | |
display: flex; | |
align-items: center; | |
flex-wrap:wrap; | |
} | |
#channelTabs-container>:not(#channelTabs-settingsMenu)+div { | |
padding-top: 4px; | |
border-top: 1px solid var(--background-modifier-accent); | |
} | |
.channelTabs-tab { | |
display: flex; | |
align-items: center; | |
height: var(--channelTabs-tabHeight); | |
background: none; | |
border-radius: 4px; | |
max-width: var(--channelTabs-tabWidth); | |
min-width: var(--channelTabs-tabWidthMin); | |
flex: 1 1 var(--channelTabs-tabWidthMin); | |
margin-bottom: 3px; | |
} | |
.channelTabs-tab>div:first-child { | |
display: flex; | |
width: calc(100% - 16px); | |
align-items: center; | |
} | |
.channelTabs-tab:not(.channelTabs-selected):hover { | |
background: var(--background-modifier-hover); | |
} | |
.channelTabs-tab:not(.channelTabs-selected):active { | |
background: var(--background-modifier-active); | |
} | |
.channelTabs-tab.channelTabs-selected { | |
background: var(--background-modifier-selected); | |
} | |
.channelTabs-tab.channelTabs-unread:not(.channelTabs-selected), | |
.channelTabs-tab.channelTabs-unread:not(.channelTabs-selected), | |
.channelTabs-tab.channelTabs-mention:not(.channelTabs-selected) { | |
color: var(--interactive-hover); | |
} | |
.channelTabs-tab.channelTabs-unread:not(.channelTabs-selected):hover, | |
.channelTabs-tab.channelTabs-mention:not(.channelTabs-selected):hover { | |
color: var(--interactive-active); | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Quick Settings | |
*/ | |
html:not(.platform-win) #channelTabs-settingsMenu { | |
margin-right: 0; | |
} | |
#channelTabs-settingsMenu { | |
position: absolute; | |
right:0; | |
width: 20px; | |
height: 20px; | |
z-index: 1000; | |
} | |
#channelTabs-settingsMenu:hover { | |
background: var(--background-modifier-hover); | |
} | |
.channelTabs-settingsIcon { | |
max-width: 40px; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
max-height: 40px; | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Tab Name | |
*/ | |
.channelTabs-tab .channelTabs-tabName { | |
margin-right: 6px; | |
font-size: var(--channelTabs-tabNameFontSize); | |
line-height: normal; | |
color: var(--interactive-normal); | |
overflow: hidden; | |
white-space: nowrap; | |
text-overflow: ellipsis; | |
} | |
.channelTabs-tab:not(.channelTabs-selected):hover .channelTabs-tabName { | |
color: var(--interactive-hover); | |
} | |
.channelTabs-tab:not(.channelTabs-selected):active .channelTabs-tabName, | |
.channelTabs-tab.channelTabs-selected .channelTabs-tabName { | |
color: var(--interactive-active); | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Tab Icon | |
*/ | |
.channelTabs-tabIcon { | |
height: 20px; | |
border-radius: 50%; | |
-webkit-user-drag: none; | |
} | |
.channelTabs-tabIconWrapper { | |
margin: 0 6px; | |
flex-shrink: 0; | |
} | |
.channelTabs-onlineIcon { | |
fill: hsl(139, calc(var(--saturation-factor, 1) * 47.3%), 43.9%); | |
mask: url(#svg-mask-status-online); | |
} | |
.channelTabs-idleIcon { | |
fill: hsl(38, calc(var(--saturation-factor, 1) * 95.7%), 54.1%); | |
mask: url(#svg-mask-status-idle); | |
} | |
.channelTabs-doNotDisturbIcon { | |
fill: hsl(359, calc(var(--saturation-factor, 1) * 82.6%), 59.4%); | |
mask: url(#svg-mask-status-dnd); | |
} | |
.channelTabs-offlineIcon { | |
fill: hsl(214, calc(var(--saturation-factor, 1) * 9.9%), 50.4%); | |
mask: url(#svg-mask-status-offline); | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Close Tab / New Tab | |
*/ | |
.channelTabs-closeTab { | |
position: relative; | |
height: 16px; | |
width: 16px; | |
flex-shrink: 0; | |
right: 6px; | |
border-radius: 4px; | |
color: var(--interactive-normal); | |
cursor: pointer; | |
} | |
.channelTabs-closeTab svg { | |
height: 100%; | |
width: 100%; | |
transform: scale(0.85); | |
} | |
.channelTabs-newTab { | |
display:flex; | |
align-items: center; | |
justify-content: center; | |
flex-shrink: 0; | |
height: var(--channelTabs-openTabSize); | |
width: 24px; | |
margin: 0 6px 3px 6px; | |
border-radius: 4px; | |
cursor: pointer; | |
color: var(--interactive-normal); | |
} | |
.channelTabs-newTab:hover { | |
background: var(--background-modifier-hover); | |
color: var(--interactive-hover); | |
} | |
.channelTabs-newTab:active { | |
background: var(--background-modifier-active); | |
color: var(--interactive-active); | |
} | |
.channelTabs-closeTab:hover { | |
background: hsl(359,calc(var(--saturation-factor, 1)*82.6%),59.4%); | |
color: white; | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Badges | |
*/ | |
.channelTabs-gridContainer { | |
display: flex; | |
margin-right: 6px; | |
} | |
.channelTabs-mentionBadge, | |
.channelTabs-unreadBadge { | |
border-radius: 8px; | |
padding: 0 4px; | |
min-width: 8px; | |
width: fit-content; | |
height: 16px; | |
font-size: 12px; | |
line-height: 16px; | |
font-weight: 600; | |
text-align: center; | |
color: #fff; | |
} | |
.channelTabs-typingBadge { | |
border-radius: 8px; | |
padding-left: 4px; | |
padding-right: 4px; | |
min-width: 8px; | |
width: fit-content; | |
height: 16px; | |
font-size: 12px; | |
line-height: 16px; | |
font-weight: 600; | |
text-align: center; | |
color: #fff; | |
} | |
.channelTabs-mentionBadge { | |
background-color: hsl(359, calc(var(--saturation-factor, 1) * 82.6%), 59.4%); | |
} | |
.channelTabs-unreadBadge { | |
background-color: hsl(235, calc(var(--saturation-factor, 1) * 86%), 65%); | |
} | |
.channelTabs-classicBadgeAlignment { | |
margin-right: 6px; | |
display: inline-block; | |
float: right; | |
} | |
.channelTabs-badgeAlignLeft { | |
float: left; | |
} | |
.channelTabs-badgeAlignRight { | |
float: right; | |
} | |
.channelTabs-tab .channelTabs-mentionBadge, | |
.channelTabs-tab .channelTabs-unreadBadge, | |
.channelTabs-tab .channelTabs-typingBadge { | |
height: 16px; | |
} | |
.channelTabs-tab .channelTabs-noMention, | |
.channelTabs-tab .channelTabs-noUnread { | |
background-color: var(--background-primary); | |
color: var(--text-muted); | |
} | |
.channelTabs-fav .channelTabs-mentionBadge, | |
.channelTabs-fav .channelTabs-unreadBadge { | |
display: inline-block; | |
vertical-align: bottom; | |
float: right; | |
margin-left: 2px; | |
} | |
.channelTabs-fav .channelTabs-typingBadge { | |
display: inline-flex; | |
vertical-align: bottom; | |
float: right; | |
margin-left: 2px; | |
margin-right: 6px; | |
} | |
.channelTabs-fav .channelTabs-noMention, | |
.channelTabs-fav .channelTabs-noUnread { | |
background-color: var(--background-primary); | |
color: var(--text-muted); | |
} | |
.channelTabs-fav .channelTabs-noTyping { | |
display: none; | |
} | |
.channelTabs-fav .channelTabs-favName + div { | |
margin-left: 6px; | |
} | |
.channelTabs-favGroupBtn .channelTabs-noMention, | |
.channelTabs-favGroupBtn .channelTabs-noUnread { | |
background-color: var(--background-primary); | |
color: var(--text-muted); | |
} | |
.channelTabs-favGroupBtn .channelTabs-typingBadge { | |
display: inline-flex; | |
vertical-align: bottom; | |
float: right; | |
margin-left: 2px; | |
} | |
.channelTabs-favGroupBtn .channelTabs-mentionBadge, | |
.channelTabs-favGroupBtn .channelTabs-unreadBadge { | |
display: inline-block; | |
vertical-align: bottom; | |
float: right; | |
margin-left: 2px; | |
} | |
.channelTabs-favGroupBtn .channelTabs-noTyping { | |
display: none; | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Favs | |
*/ | |
.channelTabs-favContainer { | |
display: flex; | |
align-items: center; | |
flex-wrap:wrap; | |
} | |
.channelTabs-fav { | |
display: flex; | |
align-items: center; | |
min-width: 0; | |
border-radius: 4px; | |
height: var(--channelTabs-favHeight); | |
background: none; | |
flex: 0 0 1; | |
max-width: var(--channelTabs-tabWidth); | |
margin-bottom: 3px; | |
padding-left: 6px; | |
padding-right: 6px; | |
} | |
.channelTabs-fav:hover { | |
background: var(--background-modifier-hover); | |
} | |
.channelTabs-fav:active { | |
background: var(--background-modifier-active); | |
} | |
.channelTabs-favIcon { | |
height: 20px; | |
border-radius: 50%; | |
-webkit-user-drag: none; | |
} | |
.channelTabs-favName { | |
margin-left: 6px; | |
font-size: var(--channelTabs-tabNameFontSize); | |
line-height: normal; | |
color: var(--interactive-normal); | |
overflow: hidden; | |
white-space: nowrap; | |
text-overflow: ellipsis; | |
} | |
.channelTabs-fav:hover .channelTabs-favName { | |
color: var(--interactive-hover); | |
} | |
.channelTabs-fav:active .channelTabs-favName { | |
color: var(--interactive-active); | |
} | |
.channelTabs-noFavNotice { | |
color: var(--text-muted); | |
font-size: 14px; | |
padding: 3px; | |
} | |
/* | |
//#endregion | |
*/ | |
/* | |
//#region Fav Folders | |
*/ | |
.channelTabs-favGroupBtn { | |
display: flex; | |
align-items: center; | |
min-width: 0; | |
border-radius: 4px; | |
height: var(--channelTabs-favHeight); | |
flex: 0 1 1; | |
max-width: var(--channelTabs-tabWidth); | |
padding: 0 6px; | |
font-size: 12px; | |
color: var(--interactive-normal); | |
overflow: hidden; | |
white-space: nowrap; | |
text-overflow: ellipsis; | |
margin-bottom: 3px; | |
} | |
.channelTabs-favGroupBtn>:first-child { | |
margin-left: 6px; | |
} | |
.channelTabs-favGroup:hover .channelTabs-favGroupBtn { | |
background: var(--background-modifier-hover); | |
} | |
.channelTabs-favGroup-content { | |
z-index: 1001; | |
display: none; | |
position: absolute; | |
min-width: max-content; | |
background-color: var(--background-floating); | |
-webkit-box-shadow: var(--elevation-high); | |
box-shadow: var(--elevation-high); | |
border-radius: 4px; | |
padding: 4px; | |
} | |
.channelTabs-favGroup-content>:last-child { | |
margin-bottom: 0; | |
} | |
.channelTabs-favGroupShow { | |
display:block; | |
} | |
.channelTabs-sliderContainer { | |
display: flex; | |
justify-content: center; | |
padding: 4px 8px; | |
margin: 2px 6px 12px 6px; | |
background: var(--slider-background-normal); | |
border-radius: var(--slider-background-radius); | |
} | |
.channelTabs-slider { | |
position: relative; | |
top: -14px; | |
} | |
.channelTabs-minimized { | |
--channelTabs-tabWidth: fit-content; | |
--channelTabs-tabWidthMin: fit-content; | |
} | |
.channelTabs-tab.channelTabs-minimized>div>:first-child~*, | |
.channelTabs-fav.channelTabs-minimized>svg:first-child~*, | |
.channelTabs-tab.channelTabs-minimized>.channelTabs-closeTab { | |
display:none; | |
} | |
/* | |
//#endregion | |
*/ | |
`; | |
if (this.settings.compactStyle === true) PluginUtilities.addStyle("channelTabs-style-compact", CompactVariables); | |
if (this.settings.compactStyle === false) PluginUtilities.addStyle("channelTabs-style-cozy", CozyVariables); | |
if (this.settings.privacyMode === true) PluginUtilities.addStyle("channelTabs-style-private", PrivacyStyle); | |
if (this.settings.radialStatusMode === true) PluginUtilities.addStyle("channelTabs-style-radialstatus", RadialStatusStyle); | |
if (this.settings.showNavButtons === true) PluginUtilities.addStyle("channelTabs-style-tabnav", tabNavStyle); | |
PluginUtilities.addStyle("channelTabs-style-constants", ConstantVariables); | |
PluginUtilities.addStyle("channelTabs-style", BaseStyle); | |
} | |
removeStyle() | |
{ | |
PluginUtilities.removeStyle("channelTabs-style-compact"); | |
PluginUtilities.removeStyle("channelTabs-style-cozy"); | |
PluginUtilities.removeStyle("channelTabs-style-private"); | |
PluginUtilities.removeStyle("channelTabs-style-radialstatus"); | |
PluginUtilities.removeStyle("channelTabs-style-tabnav"); | |
PluginUtilities.removeStyle("channelTabs-style-constants"); | |
PluginUtilities.removeStyle("channelTabs-style"); | |
} | |
//#endregion | |
//#region Init/Default Functions | |
ifNoTabsExist() | |
{ | |
if(this.settings.tabs.length == 0) this.settings.tabs = [{ | |
name: getCurrentName(), | |
url: location.pathname, | |
selected: true, | |
iconUrl: getCurrentIconUrl() | |
}]; | |
} | |
ifReopenLastChannelDefault() | |
{ | |
if(this.settings.reopenLastChannel) | |
{ | |
switching = true; | |
NavigationUtils.transitionTo((this.settings.tabs.find(tab=>tab.selected) || this.settings.tabs[0]).url); | |
switching = false; | |
} | |
} | |
//#endregion | |
//#region Patches | |
async patchAppView(promiseState) | |
{ | |
const AppView = await ReactComponents.getComponent("Shakeable", ".app-2CXKsg"); | |
if(promiseState.cancelled) return; | |
Patcher.after(AppView.component.prototype, "render", (thisObject, _, returnValue) => { | |
returnValue.props.children = [ | |
React.createElement(TopBar, { | |
showTabBar: this.settings.showTabBar, | |
showFavBar: this.settings.showFavBar, | |
showFavUnreadBadges: this.settings.showFavUnreadBadges, | |
showFavMentionBadges: this.settings.showFavMentionBadges, | |
showFavTypingBadge: this.settings.showFavTypingBadge, | |
showEmptyFavBadges: this.settings.showEmptyFavBadges, | |
showTabUnreadBadges: this.settings.showTabUnreadBadges, | |
showTabMentionBadges: this.settings.showTabMentionBadges, | |
showTabTypingBadge: this.settings.showTabTypingBadge, | |
showEmptyTabBadges: this.settings.showEmptyTabBadges, | |
showActiveTabUnreadBadges: this.settings.showActiveTabUnreadBadges, | |
showActiveTabMentionBadges: this.settings.showActiveTabMentionBadges, | |
showActiveTabTypingBadge: this.settings.showActiveTabTypingBadge, | |
showEmptyActiveTabBadges: this.settings.showEmptyActiveTabBadges, | |
showFavGroupUnreadBadges: this.settings.showFavGroupUnreadBadges, | |
showFavGroupMentionBadges: this.settings.showFavGroupMentionBadges, | |
showFavGroupTypingBadge: this.settings.showFavGroupTypingBadge, | |
showEmptyFavGroupBadges: this.settings.showEmptyFavGroupBadges, | |
compactStyle: this.settings.compactStyle, | |
privacyMode: this.settings.privacyMode, | |
radialStatusMode: this.settings.radialStatusMode, | |
tabWidthMin: this.settings.tabWidthMin, | |
showQuickSettings: this.settings.showQuickSettings, | |
showNavButtons: this.settings.showNavButtons, | |
alwaysFocusNewTabs: this.settings.alwaysFocusNewTabs, | |
useStandardNav: this.settings.useStandardNav, | |
tabs: this.settings.tabs, | |
favs: this.settings.favs, | |
favGroups: this.settings.favGroups, | |
ref: TopBarRef, | |
plugin: this | |
}), | |
returnValue.props.children | |
].flat(); | |
}); | |
const forceUpdate = ()=>{ | |
const { app } = WebpackModules.getByProps("app", "layers") || {}; | |
const query = document.querySelector(`.${app}`); | |
if(query) ReactTools.getOwnerInstance(query)?.forceUpdate?.(); | |
}; | |
forceUpdate(); | |
patches.push(()=>forceUpdate()); | |
} | |
patchContextMenus() | |
{ | |
patches.push( | |
ContextMenu.patch("channel-context", (returnValue, props) => { | |
if(!this.settings.showTabBar && !this.settings.showFavBar) return; | |
returnValue.props.children.push(CreateTextChannelContextMenuChildren(this, props)); | |
}), | |
ContextMenu.patch("user-context", (returnValue, props) => { | |
if(!this.settings.showTabBar && !this.settings.showFavBar) return; | |
if(!returnValue) return; | |
if (!props.channel || props.channel.recipients.length !== 1 || props.channel.recipients[0] !== props.user.id) return; | |
returnValue.props.children.push(CreateDMContextMenuChildren(this, props)); | |
}), | |
ContextMenu.patch("gdm-context", (returnValue, props) => { | |
if(!this.settings.showTabBar && !this.settings.showFavBar) return; | |
if(!returnValue) return; | |
returnValue.props.children.push(CreateGroupContextMenuChildren(this, props)); | |
}), | |
ContextMenu.patch("guild-context", (returnValue, props) => { | |
if(!this.settings.showTabBar && !this.settings.showFavBar) return; | |
const channel = ChannelStore.getChannel(SelectedChannelStore.getChannelId(props.guild.id)); | |
returnValue.props.children.push(CreateGuildContextMenuChildren(this, props, channel)); | |
}) | |
); | |
} | |
//#endregion | |
//#region Handlers | |
clickHandler(e) | |
{ | |
if (!e.target.matches('.channelTabs-favGroupBtn')) { | |
closeAllDropdowns(); | |
} | |
} | |
keybindHandler(e) | |
{ | |
const keybinds = [ | |
{altKey: false, ctrlKey: true, shiftKey: false, keyCode: 87 /*w*/, action: this.closeCurrentTab}, | |
{altKey: false, ctrlKey: true, shiftKey: false, keyCode: 33 /*pg_up*/, action: this.previousTab}, | |
{altKey: false, ctrlKey: true, shiftKey: false, keyCode: 34 /*pg_down*/, action: this.nextTab} | |
]; | |
keybinds.forEach(keybind => { | |
if(e.altKey === keybind.altKey && e.ctrlKey === keybind.ctrlKey && e.shiftKey === keybind.shiftKey && e.keyCode === keybind.keyCode) keybind.action(); | |
}) | |
} | |
//#endregion | |
//#region General Functions | |
onSwitch(){ | |
if(switching) return; | |
//console.log(this); | |
if(TopBarRef.current){ | |
TopBarRef.current.setState({ | |
tabs: TopBarRef.current.state.tabs.map(tab => { | |
if(tab.selected){ | |
const channelId = SelectedChannelStore.getChannelId(); | |
return { | |
name: getCurrentName(), | |
url: location.pathname, | |
selected: true, | |
currentStatus: getCurrentUserStatus(location.pathname), | |
iconUrl: getCurrentIconUrl(location.pathname), | |
channelId: channelId, | |
minimized: this.settings.tabs[this.settings.tabs.findIndex(tab=>tab.selected)].minimized | |
}; | |
}else{ | |
return Object.assign({}, tab); | |
} | |
}) | |
}, this.saveSettings); | |
}else if(!this.settings.reopenLastChannel){ | |
const channelId = SelectedChannelStore.getChannelId(); | |
this.settings.tabs[this.settings.tabs.findIndex(tab=>tab.selected)] = { | |
name: getCurrentName(), | |
url: location.pathname, | |
selected: true, | |
currentStatus: getCurrentUserStatus(location.pathname), | |
iconUrl: getCurrentIconUrl(location.pathname), | |
channelId: channelId, | |
minimized: this.settings.tabs[this.settings.tabs.findIndex(tab=>tab.selected)].minimized | |
}; | |
} | |
} | |
mergeItems(itemsTab, itemsFav){ | |
const out = []; | |
if(this.settings.showTabBar) out.push(...itemsTab); | |
if(this.settings.showFavBar) out.push(...itemsFav); | |
return out; | |
} | |
//#endregion | |
//#region Hotkey Functions | |
nextTab(){ | |
if(TopBarRef.current) TopBarRef.current.switchToTab((TopBarRef.current.state.selectedTabIndex + 1) % TopBarRef.current.state.tabs.length); | |
} | |
previousTab(){ | |
if(TopBarRef.current) TopBarRef.current.switchToTab((TopBarRef.current.state.selectedTabIndex - 1 + TopBarRef.current.state.tabs.length) % TopBarRef.current.state.tabs.length); | |
} | |
closeCurrentTab(){ | |
if(TopBarRef.current) TopBarRef.current.closeTab(TopBarRef.current.state.selectedTabIndex); | |
} | |
//#endregion | |
//#region Settings | |
get defaultVariables(){ | |
return { | |
tabs: [], | |
favs: [], | |
favGroups: [], | |
showTabBar: true, | |
showFavBar: true, | |
reopenLastChannel: false, | |
showFavUnreadBadges: true, | |
showFavMentionBadges: true, | |
showFavTypingBadge: true, | |
showEmptyFavBadges: false, | |
showTabUnreadBadges: true, | |
showTabMentionBadges: true, | |
showTabTypingBadge: true, | |
showEmptyTabBadges: false, | |
showActiveTabUnreadBadges: false, | |
showActiveTabMentionBadges: false, | |
showActiveTabTypingBadge: false, | |
showEmptyActiveTabBadges: false, | |
compactStyle: false, | |
privacyMode: false, | |
radialStatusMode: false, | |
tabWidthMin: 100, | |
showFavGroupUnreadBadges: true, | |
showFavGroupMentionBadges: true, | |
showFavGroupTypingBadge: true, | |
showEmptyFavGroupBadges: false, | |
showQuickSettings: true, | |
showNavButtons: true, | |
alwaysFocusNewTabs: false, | |
useStandardNav: true | |
}; | |
} | |
getSettingsPath(useOldLocation) | |
{ | |
if (useOldLocation === true) | |
{ | |
return this.getName(); | |
} | |
else | |
{ | |
const user_id = UserStore.getCurrentUser()?.id; | |
return this.getName() + "_new" + (user_id != null ? "_" + user_id : ""); | |
} | |
} | |
loadSettings() | |
{ | |
if (Object.keys(PluginUtilities.loadSettings(this.getSettingsPath())).length === 0) | |
{ | |
this.settings = PluginUtilities.loadSettings(this.getSettingsPath(true), this.defaultVariables); | |
} | |
else | |
{ | |
this.settings = PluginUtilities.loadSettings(this.getSettingsPath(), this.defaultVariables); | |
} | |
this.settings.favs = this.settings.favs.map(fav => { | |
if(fav.channelId === undefined){ | |
const match = fav.url.match(/^\/channels\/[^\/]+\/(\d+)$/); | |
if(match) return Object.assign(fav, {channelId: match[1]}); | |
} | |
if (fav.groupId === undefined) | |
{ | |
return Object.assign(fav, {groupId: -1}); | |
} | |
return fav; | |
}); | |
this.saveSettings(); | |
} | |
saveSettings(){ | |
if(TopBarRef.current){ | |
this.settings.tabs = TopBarRef.current.state.tabs; | |
this.settings.favs = TopBarRef.current.state.favs; | |
this.settings.favGroups = TopBarRef.current.state.favGroups; | |
} | |
PluginUtilities.saveSettings(this.getSettingsPath(), this.settings); | |
} | |
getSettingsPanel(){ | |
const panel = document.createElement("div"); | |
panel.className = "form"; | |
panel.style = "width:100%;"; | |
//#region Startup Settings | |
new Settings.SettingGroup("Startup Settings", {shown: true}).appendTo(panel) | |
.append(new Settings.Switch("Reopen last channel", "When starting the plugin (or discord) the channel will be selected again instead of the friends page", this.settings.reopenLastChannel, checked=>{ | |
this.settings.reopenLastChannel = checked; | |
this.saveSettings(); | |
})); | |
//#endregion | |
//#region General Appearance | |
new Settings.SettingGroup("General Appearance").appendTo(panel) | |
.append(new Settings.Switch("Show Tab Bar", "Allows you to have multiple tabs like in a web browser", this.settings.showTabBar, checked=>{ | |
this.settings.showTabBar = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showTabBar: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Fav Bar", "Allows you to add favorites by right clicking a tab or the fav bar", this.settings.showFavBar, checked=>{ | |
this.settings.showFavBar = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavBar: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Quick Settings", "Allows you to quickly change major settings from a context menu", this.settings.showQuickSettings, checked=>{ | |
this.settings.showQuickSettings = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showQuickSettings: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Navigation Buttons", "Click to go the left or right tab, this behavior can be changed in Behavior settings", this.settings.showNavButtons, checked=>{ | |
this.settings.showNavButtons = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showNavButtons: checked | |
}); | |
this.removeStyle(); | |
this.applyStyle(); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Use Compact Look", "", this.settings.compactStyle, checked=>{ | |
this.settings.compactStyle = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
compactStyle: checked | |
}); | |
this.removeStyle(); | |
this.applyStyle(); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Enable Privacy Mode", "Obfusicates all the Sensitive Text in ChannelTabs", this.settings.privacyMode, checked=>{ | |
this.settings.privacyMode = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
privacyMode: checked | |
}); | |
this.removeStyle(); | |
this.applyStyle(); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Use Radial Status Indicators", "Changes the status indicator into a circular border", this.settings.radialStatusMode, checked=>{ | |
this.settings.radialStatusMode = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
radialStatusMode: checked | |
}); | |
this.removeStyle(); | |
this.applyStyle(); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Slider("Minimum Tab Width", "Set the limit on how small a tab can be before overflowing to a new row", | |
58, 220, | |
this.settings.tabWidthMin, | |
value => ( | |
this.settings.tabWidthMin = Math.round(value), | |
this.saveSettings(), | |
document.documentElement.style.setProperty("--channelTabs-tabWidthMin", this.settings.tabWidthMin + "px") | |
), | |
{ | |
defaultValue: 100, | |
markers: [60, 85, 100, 125, 150, 175, 200, 220], | |
units: 'px' | |
} | |
)); | |
//#endregion | |
//#region Behavior Settings | |
new Settings.SettingGroup("Behavior").appendTo(panel) | |
.append(new Settings.Switch("Always Auto Focus New Tabs", "Forces all newly created tabs to bring themselves to focus", this.settings.alwaysFocusNewTabs, checked=>{ | |
this.settings.alwaysFocusNewTabs = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
alwaysFocusNewTabs: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Primary Forward/Back Navigation", "Instead of scrolling down the row, use the previous and next buttons to navigate between pages", this.settings.useStandardNav, checked=>{ | |
this.settings.useStandardNav = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
useStandardNav: checked | |
}); | |
this.saveSettings(); | |
})); | |
//#endregion | |
//#region Badge Visibility - Favs | |
new Settings.SettingGroup("Badge Visibility - Favorites").appendTo(panel) | |
.append(new Settings.Switch("Show Unread", "", this.settings.showFavUnreadBadges, checked=>{ | |
this.settings.showFavUnreadBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavUnreadBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Mentions", "", this.settings.showFavMentionBadges, checked=>{ | |
this.settings.showFavMentionBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavMentionBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Typing", "", this.settings.showFavTypingBadge, checked=>{ | |
this.settings.showFavTypingBadge = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavTypingBadge: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Empty", "", this.settings.showEmptyFavBadges, checked=>{ | |
this.settings.showEmptyFavBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showEmptyFavBadges: checked | |
}); | |
this.saveSettings(); | |
})); | |
//#endregion | |
//#region Badge Visibility - Fav Groups | |
new Settings.SettingGroup("Badge Visibility - Favorite Groups").appendTo(panel) | |
.append(new Settings.Switch("Show Unread", "", this.settings.showFavGroupUnreadBadges, checked=>{ | |
this.settings.showFavGroupUnreadBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavGroupUnreadBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Mentions", "", this.settings.showFavGroupMentionBadges, checked=>{ | |
this.settings.showFavGroupMentionBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavGroupMentionBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Typing", "", this.settings.showFavGroupTypingBadge, checked=>{ | |
this.settings.showFavGroupTypingBadge = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showFavGroupTypingBadge: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Empty", "", this.settings.showEmptyGroupFavBadges, checked=>{ | |
this.settings.showEmptyGroupFavBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showEmptyGroupFavBadges: checked | |
}); | |
this.saveSettings(); | |
})); | |
//#endregion | |
//#region Badge Visibility - Tabs | |
new Settings.SettingGroup("Badge Visibility - Tabs").appendTo(panel) | |
.append(new Settings.Switch("Show Unread", "", this.settings.showTabUnreadBadges, checked=>{ | |
this.settings.showTabUnreadBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showTabUnreadBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Mentions", "", this.settings.showTabMentionBadges, checked=>{ | |
this.settings.showTabMentionBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showTabMentionBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Typing", "", this.settings.showTabTypingBadge, checked=>{ | |
this.settings.showTabTypingBadge = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showTabTypingBadge: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Empty", "", this.settings.showEmptyTabBadges, checked=>{ | |
this.settings.showEmptyTabBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showEmptyTabBadges: checked | |
}); | |
this.saveSettings(); | |
})); | |
//#endregion | |
//#region Badge Visibility - Active Tabs | |
new Settings.SettingGroup("Badge Visibility - Active Tabs").appendTo(panel) | |
.append(new Settings.Switch("Show Unread", "", this.settings.showActiveTabUnreadBadges, checked=>{ | |
this.settings.showActiveTabUnreadBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showActiveTabUnreadBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Mentions", "", this.settings.showActiveTabMentionBadges, checked=>{ | |
this.settings.showActiveTabMentionBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showActiveTabMentionBadges: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Typing", "", this.settings.showActiveTabTypingBadge, checked=>{ | |
this.settings.showActiveTabTypingBadge = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showActiveTabTypingBadge: checked | |
}); | |
this.saveSettings(); | |
})) | |
.append(new Settings.Switch("Show Empty", "", this.settings.showEmptyActiveTabBadges, checked=>{ | |
this.settings.showEmptyActiveTabBadges = checked; | |
if(TopBarRef.current) TopBarRef.current.setState({ | |
showEmptyActiveTabBadges: checked | |
}); | |
this.saveSettings(); | |
})); | |
//#endregion | |
return panel; | |
} | |
//#endregion | |
} | |
//#endregion | |
}; | |
return plugin(Plugin, Api); | |
})(global.ZeresPluginLibrary.buildPlugin(config)); | |
})(); |
GM guys. If anyone got a fix, save us. Thank you
If you are looking for a non-crash version ==new Repo==> https://github.com/samfundev/BetterDiscordStuff
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ShowFabTypingBadge: YES gives me an instant crash. Think this still requires fixing.