Plugin
ShowHiddenChannels
Show channels that you do not have access to view.
1
import "./style.css";2
3
import { definePluginSettings } from "@api/Settings";4
import ErrorBoundary from "@components/ErrorBoundary";5
import { Devs } from "@utils/constants";6
import { classNameFactory } from "@utils/css";7
import { classes } from "@utils/misc";8
import definePlugin, { OptionType } from "@utils/types";9
import type { Channel, Role } from "@vencord/discord-types";10
import { findCssClassesLazy } from "@webpack";11
import { ChannelStore, PermissionsBits, PermissionStore, Tooltip } from "@webpack/common";12
13
import HiddenChannelLockScreen, { setChannelBeginHeader } from "./components/HiddenChannelLockScreen";14
15
export const cl = classNameFactory("vc-shc-");16
17
const ChannelListClasses = findCssClassesLazy("modeSelected", "modeMuted", "unread", "icon");18
19
const enum ShowMode {20
LockIcon,21
HiddenIconWithMutedStyle22
}23
24
const CONNECT = 1n << 20n;25
26
export const settings = definePluginSettings({27
hideUnreads: {28
description: "Hide Unreads",29
type: OptionType.BOOLEAN,30
default: true,31
restartNeeded: true32
},33
showMode: {34
description: "The mode used to display hidden channels.",35
type: OptionType.SELECT,36
options: [37
{ label: "Plain style with Lock Icon instead", value: ShowMode.LockIcon, default: true },38
{ label: "Muted style with hidden eye icon on the right", value: ShowMode.HiddenIconWithMutedStyle },39
],40
restartNeeded: true41
},42
defaultAllowedUsersAndRolesDropdownState: {43
description: "Whether the allowed users and roles dropdown on hidden channels should be open by default",44
type: OptionType.BOOLEAN,45
default: true46
}47
});48
49
function isUncategorized(objChannel: { channel: Channel; comparator: number; }) {50
return objChannel.channel.id === "null" && objChannel.channel.name === "Uncategorized" && objChannel.comparator === -1;51
}52
53
export default definePlugin({54
name: "ShowHiddenChannels",55
description: "Show channels that you do not have access to view.",56
tags: ["Servers", "Utility"],57
authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX, Devs.Ven, Devs.Nuckyz, Devs.Nickyux, Devs.dzshn],58
settings,59
60
patches: [61
{62
// RenderLevel defines if a channel is hidden, collapsed in category, visible, etc63
find: 039;"placeholder-channel-id"039;,64
replacement: [65
// Remove the special logic for channels we don't have access to66
{67
match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\[\]}}/,68
replace: ""69
},70
// Do not check for unreads when selecting the render level if the channel is hidden71
{72
match: /(?<=&&)(?=!\i\.\i\.hasUnread\(this\.record\.id\))/,73
replace: "$self.isHiddenChannel(this.record)||"74
},75
// Make channels we dont have access to be the same level as normal ones76
{77
match: /(this\.record\)\?{renderLevel:(.+?),threadIds.+?renderLevel:).+?(?=,threadIds)/g,78
replace: (_, rest, defaultRenderLevel) => `${rest}${defaultRenderLevel}`79
},80
// Remove permission checking for getRenderLevel function81
{82
match: /(getRenderLevel\(\i\){.+?return)!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL,this\.record\)\|\|/,83
replace: (_, rest) => `${rest} `84
}85
]86
},87
{88
find: "VoiceChannel, transitionTo: Channel does not have a guildId",89
replacement: [90
{91
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel92
match: /(?<=getIgnoredUsersForVoiceChannel\((\i)\.id\)[^;]+?;return\()/,93
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`94
},95
{96
// Prevent Discord from trying to connect to hidden voice channels97
match: /(?=\|\|\i\.\i\.selectVoiceChannel\((\i)\.id\))/,98
replace: (_, channel) => `||$self.isHiddenChannel(${channel})`99
},100
{101
// Make Discord show inside the channel if clicking on a hidden or locked channel102
match: /!__OVERLAY__&&\((?<=selectVoiceChannel\((\i)\.id\).+?)/,103
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel},true)||`104
}105
]106
},107
// Prevent Discord from trying to connect to hidden stage channels108
{109
find: ".AUDIENCE),{isSubscriptionGated",110
replacement: {111
match: /(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/,112
replace: (m, channel) => `${m}||$self.isHiddenChannel(${channel})`113
}114
},115
{116
find: 039;tutorialId:"instant-invite"039;,117
replacement: [118
// Render null instead of the buttons if the channel is hidden119
...[120
"renderEditButton",121
"renderInviteButton",122
].map(func => ({123
match: new RegExp(`(?<=${func}\\(\\){)`, "g"), class="ts-cmt">// Global because Discord has multiple declarations of the same functions124
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"125
}))126
]127
},128
{129
find: "VoiceChannel.renderPopout: There must always be something to render",130
all: true,131
// Render null instead of the buttons if the channel is hidden132
replacement: {133
match: /(?<=renderOpenChatButton(?:",|=)\(\)=>{)/,134
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"135
}136
},137
{138
find: "#{intl::CHANNEL_TOOLTIP_DIRECTORY}",139
predicate: () => settings.store.showMode === ShowMode.LockIcon,140
replacement: {141
// Lock Icon142
match: /(?<=(\i)\.isNSFW\(\);)switch\(\i\.type\).{0,15}\.GUILD_ANNOUNCEMENT/,143
replace: (m, channel) => `if($self.isHiddenChannel(${channel}))return $self.LockIcon;${m}`144
}145
},146
{147
find: "UNREAD_IMPORTANT:",148
predicate: () => settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,149
replacement: [150
// Make the channel appear as muted if it's hidden151
{152
match: /Children\.count.+?;(?=return\(0,\i\.jsxs?\)\(\i\.\i,{focusTarget:)(?<={channel:(\i),name:\i,muted:(\i).+?;)/,153
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`154
},155
// Add the hidden eye icon if the channel is hidden156
{157
match: /\.Children\.count.+?:null(?<=,channel:(\i).+?)/,158
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null`159
},160
// Make voice channels also appear as muted if they are muted161
{162
match: /(?<=\?\i\.\i:\i\.\i,)(.{0,150}?)if\((\i)(?:\)return |\?)(\i\.MUTED)/,163
replace: (_, otherClasses, isMuted, mutedClassExpression) => `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return ""`164
}165
]166
},167
{168
find: "UNREAD_IMPORTANT:",169
replacement: [170
{171
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden172
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,173
match: /(?<=\.LOCKED;if\()(?<={channel:(\i).+?)/,174
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`175
},176
{177
// Hide unreads178
predicate: () => settings.store.hideUnreads === true,179
match: /Children\.count.+?;(?=return\(0,\i\.jsxs?\)\(\i\.\i,{focusTarget:)(?<={channel:(\i),name:\i,.+?unread:(\i).+?)/,180
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`181
}182
]183
},184
{185
// Hide the new version of unreads box for hidden channels186
find: 039;"ChannelListUnreadsStore"039;,187
replacement: {188
match: /(?<=\.id\)\))(?=&&\(0,\i\.\i\)\((\i)\))/,189
replace: (_, channel) => `&&!$self.isHiddenChannel(${channel})`190
}191
},192
{193
// Make the old version of unreads box not visible for hidden channels194
find: "renderBottomUnread(){",195
replacement: {196
match: /(?<=!0\))(?=&&\(0,\i\.\i\)\((\i\.record)\))/,197
replace: "&&!$self.isHiddenChannel($1)"198
}199
},200
{201
// Make the state of the old version of unreads box not include hidden channels202
find: "GUILD_EVENT)}),[",203
replacement: {204
match: /(?<=\.id\)\))(?=&&\(0,\i\.\i\)\((\i)\))/,205
replace: "&&!$self.isHiddenChannel($1)"206
}207
},208
// Only render the channel header and buttons that work when transitioning to a hidden channel209
{210
find: "Missing channel in Channel.renderHeaderToolbar",211
replacement: [212
{213
match: /renderHeaderToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_TEXT:(?=.+?(\i\.push.{0,50}channel:(\i)},"notifications"\)\)))(?<=isLurking:(\i).+?)/,214
replace: (m, pushNotificationButtonExpression, channel, isLurking) => `${m}if(!${isLurking}&&$self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}`215
},216
{217
match: /renderHeaderToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_MEDIA:(?=.+?(\i\.push.{0,40}channel:(\i)},"notifications"\)\)))(?<=isLurking:(\i).+?)/,218
replace: (m, pushNotificationButtonExpression, channel, isLurking) => `${m}if(!${isLurking}&&$self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}`219
},220
{221
match: /renderMobileToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_DIRECTORY:(?<=let{channel:(\i).+?)/,222
replace: (m, channel) => `${m}if($self.isHiddenChannel(${channel}))break;`223
},224
{225
match: /(?<=renderHeaderBar(?:",|=)\(\)=>{.+?hideSearch:(\i)\.isDirectory\(\))/,226
replace: (_, channel) => `||$self.isHiddenChannel(${channel})`227
},228
{229
match: /(?<=renderSidebar\(\){)/,230
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"231
},232
{233
match: /(?<=renderChat\(\){)/,234
replace: "if($self.isHiddenChannel(this?.props?.channel))return $self.HiddenChannelLockScreen(this?.props?.channel);"235
}236
]237
},238
// Avoid trying to fetch messages from hidden channels239
{240
find: 039;"MessageManager"039;,241
replacement: {242
match: /forceFetch:\i,isPreload:.+?}=\i;(?=.+?getChannel\((\i)\))/,243
replace: (m, channelId) => `${m}if($self.isHiddenChannel({channelId:${channelId}}))return;`244
}245
},246
// Patch keybind handlers so you can't accidentally jump to hidden channels247
{248
find: 039;"alt+shift+down"039;,249
replacement: {250
match: /(?<=getChannel\(\i\);return null!=(\i))(?=.{0,200}?>0\)&&\(0,\i\.\i\)\(\i\))/,251
replace: (_, channel) => `&&!$self.isHiddenChannel(${channel})`252
}253
},254
// Patch keybind handlers so you can't accidentally jump to hidden channels255
{256
find: ".APPLICATION_STORE&&null!=",257
replacement: {258
match: /getState\(\)\.channelId.+?(?=\.map\(\i=>\i\.id)/,259
replace: "$&.filter(e=>!$self.isHiddenChannel(e))"260
}261
},262
{263
find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",264
replacement: [265
{266
// Change the role permission check to CONNECT if the channel is locked267
match: /(forceRoles:.+?)(\i\.\i\(\i\.\i\.ADMINISTRATOR,\i\.\i\.VIEW_CHANNEL\))(?<=context:(\i)}.+?)/,268
replace: (_, rest, mergedPermissions, channel) => `${rest}$self.swapViewChannelWithConnectPermission(${mergedPermissions},${channel})`269
},270
{271
// Change the permissionOverwrite check to CONNECT if the channel is locked272
match: /permissionOverwrites\[.+?\i=(?<=context:(\i)}.+?)(?=(.+?)VIEW_CHANNEL)/,273
replace: (m, channel, permCheck) => `${m}!Vencord.Webpack.Common.PermissionStore.can(${CONNECT}n,${channel})?${permCheck}CONNECT):`274
},275
{276
// Include the @everyone role in the allowed roles list for Hidden Channels277
match: /getSortedRoles.+?\.filter\(\i=>(?=!)/,278
replace: m => `${m}$self.isHiddenChannel(arguments[0]?.channel)?true:`279
},280
{281
// If the @everyone role has the required permissions, make the array only contain it282
match: /forceRoles:.+?.value\(\)(?<=channel:(\i).+?)/,283
replace: (m, channel) => `${m}.reduce(...$self.makeAllowedRolesReduce(${channel}.guild_id))`284
},285
{286
// Patch the header to only return allowed users and roles if it's a hidden channel or locked channel (Like when it's used on the HiddenChannelLockScreen)287
match: /return\(0,\i\.jsxs?\)\(\i\.\i,{channelId:(\i)\.id,children:\[(?=.{0,1000}?(\(0,\i\.jsxs?\)\("div",{className:\i\.\i,children:\[.{0,100}\i\.length>0.+?\]}\)),)/,288
replace: (m, channel, allowedUsersAndRolesComponent) => `if($self.isHiddenChannel(${channel},true)){return${allowedUsersAndRolesComponent};}${m}`289
},290
{291
// Export the channel for the users allowed component patch292
match: /maxUsers:\d+?,users:\i(?<=channel:(\i).+?)/,293
replace: (m, channel) => `${m},shcChannel:${channel}`294
},295
{296
// Always render the component for multiple allowed users297
match: /1!==\i\.length(?=\|\|)/,298
replace: "true"299
}300
]301
},302
{303
find: 039;="interactive-text-default",overflowCountClassName:039;,304
replacement: [305
{306
// Create a variable for the channel prop307
match: /let{users:\i,maxUsers:\i,/,308
replace: "let{shcChannel}=arguments[0];$&"309
},310
{311
// Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen312
match: /\i>0(?=&&!\i&&!\i)/,313
replace: m => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)?true:${m})`314
},315
{316
// Show only the plus text without overflowed children amount317
// if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen318
match: /(?<=`\+\$\{)\i(?=\})/,319
replace: overflowTextAmount => "" +320
`$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&(${overflowTextAmount}-1)<=0?"":${overflowTextAmount}`321
}322
]323
},324
{325
find: "#{intl::CHANNEL_CALL_CURRENT_SPEAKER}",326
replacement: [327
{328
// Remove the open chat button for the HiddenChannelLockScreen329
match: /(?<=&&)\i\.push\(.{0,120}"chat-spacer"/,330
replace: "(arguments[0]?.inCall||!$self.isHiddenChannel(arguments[0]?.channel,true))&&$&"331
}332
]333
},334
{335
find: "#{intl::EMBEDDED_ACTIVITIES_DEVELOPER_ACTIVITY_SHELF_FETCH_ERROR}",336
replacement: [337
{338
// Render our HiddenChannelLockScreen component instead of the main voice channel component339
match: /renderContent\(\i\){.+?this\.renderVoiceChannelEffects.+?children:/,340
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?$self.HiddenChannelLockScreen(this?.props?.channel):"341
},342
{343
// Disable gradients for the HiddenChannelLockScreen of voice channels344
match: /renderContent\(\i\){.+?disableGradients:/,345
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)||"346
},347
{348
// Disable useless components for the HiddenChannelLockScreen of voice channels349
match: /(?:{|,)render(?!Header|ExternalHeader).{0,30}?:/g,350
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?()=>null:"351
},352
{353
// Disable bad CSS class which mess up hidden voice channels styling354
match: /(?=\i\|\|\i!==\i\.\i\.FULL_SCREEN.{0,100}?this\._callContainerRef)/,355
replace: 039;$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?"":039;356
}357
]358
},359
{360
find: 039;"HasBeenInStageChannel"039;,361
replacement: [362
{363
// Render our HiddenChannelLockScreen component instead of the main stage channel component364
match: /screenMessage:(\i)\?.+?children:(?=!\1)(?<=let \i,{channel:(\i).+?)/,365
replace: (m, _isPopoutOpen, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):`366
},367
{368
// Disable useless components for the HiddenChannelLockScreen of stage channels369
match: /render(?:BottomLeft|BottomCenter|BottomRight|ChatToasts):\(\)=>(?<=let \i,{channel:(\i).+?)/g,370
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?null:`371
},372
{373
// Disable gradients for the HiddenChannelLockScreen of stage channels374
match: /"124px".+?disableGradients:(?<=let \i,{channel:(\i).+?)/,375
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})||`376
},377
{378
// Disable strange styles applied to the header for the HiddenChannelLockScreen of stage channels379
match: /"124px".+?style:(?<=let \i,{channel:(\i).+?)/,380
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?void 0:`381
}382
]383
},384
{385
find: "#{intl::STAGE_FULL_MODERATOR_TITLE}",386
replacement: [387
{388
// Remove the divider and amount of users in stage channel components for the HiddenChannelLockScreen389
match: /\(0,\i\.jsx\)\(\i\.\i\.Divider.+?}\)]}\)(?=.+?:(\i)\.guild_id)/,390
replace: (m, channel) => `$self.isHiddenChannel(${channel})?null:(${m})`391
},392
{393
// Remove the open chat button for the HiddenChannelLockScreen394
match: /(?<=numRequestToSpeak:\i\}\)\}\):null,!\i&&)\(0,\i\.jsxs?\).{0,280}?iconClassName:/,395
replace: "!$self.isHiddenChannel(arguments[0]?.channel,true)&&$&"396
}397
]398
},399
{400
// Make the chat input bar channel list contain hidden channels401
find: ",queryStaticRouteChannels(",402
replacement: [403
{404
// Make the getChannels call to GuildChannelStore return hidden channels405
match: /(?<=queryChannels\(\i\){.+?getChannels\(\i)(?=\))/,406
replace: ",true"407
},408
{409
// Avoid filtering out hidden channels from the channel list410
match: /(?<=queryChannels\(\i\){.+?\)\((\i)\.type\))(?=&&!\i\.\i\.can\()/,411
replace: "&&!$self.isHiddenChannel($1)"412
}413
]414
},415
{416
find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"",417
replacement: {418
// Make mentions of hidden channels work419
match: /\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL,\i\)/,420
replace: "true"421
},422
},423
{424
find: 039;getConfig({location:"channel_mention"})039;,425
replacement: {426
// Show inside voice channel instead of trying to join them when clicking on a channel mention427
match: /(?<=getChannel\(\i\);if\(null!=(\i)).{0,200}?return void (?=\i\.default\.selectVoiceChannel)/,428
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`429
}430
},431
{432
find: 039;"GuildChannelStore"039;,433
replacement: [434
{435
// Make GuildChannelStore contain hidden channels436
match: /isChannelGated\(.+?\)(?=&&)/,437
replace: m => `${m}&&false`438
},439
{440
// Filter hidden channels from GuildChannelStore.getChannels unless told otherwise441
match: /(?<=getChannels\(\i)(\){.*?)return (.+?)}/,442
replace: (_, rest, channels) => `,shouldIncludeHidden${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden??arguments[0]==="@favorites");}`443
},444
]445
},446
{447
find: "GuildTooltip - ",448
replacement: {449
// Make GuildChannelStore.getChannels return hidden channels450
match: /(?<=getChannels\(\i)(?=\))/,451
replace: ",true"452
}453
},454
{455
find: 039;"NowPlayingViewStore"039;,456
replacement: {457
// Make active now voice states on hidden channels458
match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/,459
replace: "$1"460
}461
},462
{463
find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",464
replacement: {465
match: /(?=function (\i)\(\i\){let{channel:.{0,200}?getSortedRoles\()/,466
replace: "$self.ChannelBeginHeader=$1;"467
}468
}469
],470
471
set ChannelBeginHeader(value: any) {472
setChannelBeginHeader(value);473
},474
475
swapViewChannelWithConnectPermission(mergedPermissions: bigint, channel: Channel) {476
if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) {477
mergedPermissions &= ~PermissionsBits.VIEW_CHANNEL;478
mergedPermissions |= PermissionsBits.CONNECT;479
}480
481
return mergedPermissions;482
},483
484
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {485
try {486
if (channel == null || Object.hasOwn(channel, "channelId") && channel.channelId == null) return false;487
488
if (channel.channelId != null) channel = ChannelStore.getChannel(channel.channelId);489
if (channel == null || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;490
if (["browse", "customize", "guide"].includes(channel.id)) return false;491
492
return !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || checkConnect && !PermissionStore.can(PermissionsBits.CONNECT, channel);493
} catch (e) {494
console.error("[ViewHiddenChannels#isHiddenChannel]: ", e);495
return false;496
}497
},498
499
resolveGuildChannels(channels: Record<string | number, Array<{ channel: Channel; comparator: number; }> | string | number>, shouldIncludeHidden: boolean) {500
if (shouldIncludeHidden) return channels;501
502
const res = {};503
for (const [key, maybeObjChannels] of Object.entries(channels)) {504
if (!Array.isArray(maybeObjChannels)) {505
res[key] = maybeObjChannels;506
continue;507
}508
509
res[key] ??= [];510
511
for (const objChannel of maybeObjChannels) {512
if (isUncategorized(objChannel) || objChannel.channel.id === null || !this.isHiddenChannel(objChannel.channel)) res[key].push(objChannel);513
}514
}515
516
return res;517
},518
519
makeAllowedRolesReduce(guildId: string) {520
return [521
(prev: Array<Role>, _: Role, index: number, originalArray: Array<Role>) => {522
if (index !== 0) return prev;523
524
const everyoneRole = originalArray.find(role => role.id === guildId);525
526
if (everyoneRole) return [everyoneRole];527
return originalArray;528
},529
[] as Array<Role>530
];531
},532
533
HiddenChannelLockScreen: (channel: any) => <HiddenChannelLockScreen channel={channel} />,534
535
LockIcon: ErrorBoundary.wrap(() => (536
<svg537
className={ChannelListClasses.icon}538
height="18"539
width="20"540
viewBox="0 0 24 24"541
aria-hidden={true}542
role="img"543
>544
<path fill="currentcolor" fillRule="evenodd" d="M17 11V7C17 4.243 14.756 2 12 2C9.242 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" />545
</svg>546
), { noop: true }),547
548
HiddenChannelIcon: ErrorBoundary.wrap(() => (549
<Tooltip text="Hidden Channel">550
{({ onMouseLeave, onMouseEnter }) => (551
<svg552
onMouseLeave={onMouseLeave}553
onMouseEnter={onMouseEnter}554
className={classes(ChannelListClasses.icon, cl("hidden-channel-icon"))}555
width="24"556
height="24"557
viewBox="0 0 24 24"558
aria-hidden={true}559
role="img"560
>561
<path fill="currentcolor" fillRule="evenodd" d="m19.8 22.6-4.2-4.15q-.875.275-1.762.413Q12.95 19 12 19q-3.775 0-6.725-2.087Q2.325 14.825 1 11.5q.525-1.325 1.325-2.463Q3.125 7.9 4.15 7L1.4 4.2l1.4-1.4 18.4 18.4ZM12 16q.275 0 .512-.025.238-.025.513-.1l-5.4-5.4q-.075.275-.1.513-.025.237-.025.512 0 1.875 1.312 3.188Q10.125 16 12 16Zm7.3.45-3.175-3.15q.175-.425.275-.862.1-.438.1-.938 0-1.875-1.312-3.188Q13.875 7 12 7q-.5 0-.938.1-.437.1-.862.3L7.65 4.85q1.025-.425 2.1-.638Q10.825 4 12 4q3.775 0 6.725 2.087Q21.675 8.175 23 11.5q-.575 1.475-1.512 2.738Q20.55 15.5 19.3 16.45Zm-4.625-4.6-3-3q.7-.125 1.288.112.587.238 1.012.688.425.45.613 1.038.187.587.087 1.162Z" />562
</svg>563
)}564
</Tooltip>565
), { noop: true })566
});567