Plugin
ShowHiddenChannels
Show channels that you do not have access to view.
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import "./style.css";8
9
import { definePluginSettings } from "@api/Settings";10
import ErrorBoundary from "@components/ErrorBoundary";11
import { Devs } from "@utils/constants";12
import { classNameFactory } from "@utils/css";13
import { classes } from "@utils/misc";14
import definePlugin, { OptionType } from "@utils/types";15
import type { Channel, Role } from "@vencord/discord-types";16
import { findCssClassesLazy } from "@webpack";17
import { ChannelStore, PermissionsBits, PermissionStore, Tooltip } from "@webpack/common";18
19
import HiddenChannelLockScreen, { setChannelBeginHeader } from "./components/HiddenChannelLockScreen";20
21
export const cl = classNameFactory("vc-shc-");22
23
const ChannelListClasses = findCssClassesLazy("modeSelected", "modeMuted", "unread", "icon");24
25
const enum ShowMode {26
LockIcon,27
HiddenIconWithMutedStyle28
}29
30
const CONNECT = 1n << 20n;31
32
export const settings = definePluginSettings({33
hideUnreads: {34
description: "Hide Unreads",35
type: OptionType.BOOLEAN,36
default: true,37
restartNeeded: true38
},39
showMode: {40
description: "The mode used to display hidden channels.",41
type: OptionType.SELECT,42
options: [43
{ label: "Plain style with Lock Icon instead", value: ShowMode.LockIcon, default: true },44
{ label: "Muted style with hidden eye icon on the right", value: ShowMode.HiddenIconWithMutedStyle },45
],46
restartNeeded: true47
},48
defaultAllowedUsersAndRolesDropdownState: {49
description: "Whether the allowed users and roles dropdown on hidden channels should be open by default",50
type: OptionType.BOOLEAN,51
default: true52
}53
});54
55
function isUncategorized(objChannel: { channel: Channel; comparator: number; }) {56
return objChannel.channel.id === "null" && objChannel.channel.name === "Uncategorized" && objChannel.comparator === -1;57
}58
59
export default definePlugin({60
name: "ShowHiddenChannels",61
description: "Show channels that you do not have access to view.",62
tags: ["Servers", "Utility"],63
authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX, Devs.Ven, Devs.Nuckyz, Devs.Nickyux, Devs.dzshn],64
settings,65
66
patches: [67
{68
// RenderLevel defines if a channel is hidden, collapsed in category, visible, etc69
find: 039;"placeholder-channel-id"039;,70
replacement: [71
// Remove the special logic for channels we don't have access to72
{73
match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\[\]}}/,74
replace: ""75
},76
// Do not check for unreads when selecting the render level if the channel is hidden77
{78
match: /(?<=&&)(?=!\i\.\i\.hasUnread\(this\.record\.id\))/,79
replace: "$self.isHiddenChannel(this.record)||"80
},81
// Make channels we dont have access to be the same level as normal ones82
{83
match: /(this\.record\)\?{renderLevel:(.+?),threadIds.+?renderLevel:).+?(?=,threadIds)/g,84
replace: (_, rest, defaultRenderLevel) => `${rest}${defaultRenderLevel}`85
},86
// Remove permission checking for getRenderLevel function87
{88
match: /(getRenderLevel\(\i\){.+?return)!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL,this\.record\)\|\|/,89
replace: (_, rest) => `${rest} `90
}91
]92
},93
{94
find: "VoiceChannel, transitionTo: Channel does not have a guildId",95
replacement: [96
{97
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel98
match: /(?<=getIgnoredUsersForVoiceChannel\((\i)\.id\)[^;]+?;return\()/,99
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`100
},101
{102
// Prevent Discord from trying to connect to hidden voice channels103
match: /(?=\|\|\i\.\i\.selectVoiceChannel\((\i)\.id\))/,104
replace: (_, channel) => `||$self.isHiddenChannel(${channel})`105
},106
{107
// Make Discord show inside the channel if clicking on a hidden or locked channel108
match: /!__OVERLAY__&&\((?<=selectVoiceChannel\((\i)\.id\).+?)/,109
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel},true)||`110
}111
]112
},113
// Prevent Discord from trying to connect to hidden stage channels114
{115
find: ".AUDIENCE),{isSubscriptionGated",116
replacement: {117
match: /(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/,118
replace: (m, channel) => `${m}||$self.isHiddenChannel(${channel})`119
}120
},121
{122
find: 039;tutorialId:"instant-invite"039;,123
replacement: [124
// Render null instead of the buttons if the channel is hidden125
...[126
"renderEditButton",127
"renderInviteButton",128
].map(func => ({129
match: new RegExp(`(?<=${func}\\(\\){)`, "g"), class="ts-cmt">// Global because Discord has multiple declarations of the same functions130
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"131
}))132
]133
},134
{135
find: "VoiceChannel.renderPopout: There must always be something to render",136
all: true,137
// Render null instead of the buttons if the channel is hidden138
replacement: {139
match: /(?<=renderOpenChatButton(?:",|=)\(\)=>{)/,140
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"141
}142
},143
{144
find: "#{intl::CHANNEL_TOOLTIP_DIRECTORY}",145
predicate: () => settings.store.showMode === ShowMode.LockIcon,146
replacement: {147
// Lock Icon148
match: /(?<=(\i)\.isNSFW\(\);)switch\(\i\.type\).{0,15}\.GUILD_ANNOUNCEMENT/,149
replace: (m, channel) => `if($self.isHiddenChannel(${channel}))return $self.LockIcon;${m}`150
}151
},152
{153
find: "UNREAD_IMPORTANT:",154
predicate: () => settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,155
replacement: [156
// Make the channel appear as muted if it's hidden157
{158
match: /Children\.count.+?;(?=return\(0,\i\.jsxs?\)\(\i\.\i,{focusTarget:)(?<={channel:(\i),name:\i,muted:(\i).+?;)/,159
replace: (m, channel, muted) => `${m}${muted}=$self.isHiddenChannel(${channel})?true:${muted};`160
},161
// Add the hidden eye icon if the channel is hidden162
{163
match: /\.Children\.count.+?:null(?<=,channel:(\i).+?)/,164
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null`165
},166
// Make voice channels also appear as muted if they are muted167
{168
match: /(?<=\?\i\.\i:\i\.\i,)(.{0,150}?)if\((\i)(?:\)return |\?)(\i\.MUTED)/,169
replace: (_, otherClasses, isMuted, mutedClassExpression) => `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return ""`170
}171
]172
},173
{174
find: "UNREAD_IMPORTANT:",175
replacement: [176
{177
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden178
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,179
match: /(?<=\.LOCKED;if\()(?<={channel:(\i).+?)/,180
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`181
},182
{183
// Hide unreads184
predicate: () => settings.store.hideUnreads === true,185
match: /Children\.count.+?;(?=return\(0,\i\.jsxs?\)\(\i\.\i,{focusTarget:)(?<={channel:(\i),name:\i,.+?unread:(\i).+?)/,186
replace: (m, channel, unread) => `${m}${unread}=$self.isHiddenChannel(${channel})?false:${unread};`187
}188
]189
},190
{191
// Hide the new version of unreads box for hidden channels192
find: 039;"ChannelListUnreadsStore"039;,193
replacement: {194
match: /(?<=\.id\)\))(?=&&\(0,\i\.\i\)\((\i)\))/,195
replace: (_, channel) => `&&!$self.isHiddenChannel(${channel})`196
}197
},198
{199
// Make the old version of unreads box not visible for hidden channels200
find: "renderBottomUnread(){",201
replacement: {202
match: /(?<=!0\))(?=&&\(0,\i\.\i\)\((\i\.record)\))/,203
replace: "&&!$self.isHiddenChannel($1)"204
}205
},206
{207
// Make the state of the old version of unreads box not include hidden channels208
find: "GUILD_EVENT)}),[",209
replacement: {210
match: /(?<=\.id\)\))(?=&&\(0,\i\.\i\)\((\i)\))/,211
replace: "&&!$self.isHiddenChannel($1)"212
}213
},214
// Only render the channel header and buttons that work when transitioning to a hidden channel215
{216
find: "Missing channel in Channel.renderHeaderToolbar",217
replacement: [218
{219
match: /renderHeaderToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_TEXT:(?=.+?(\i\.push.{0,50}channel:(\i)},"notifications"\)\)))(?<=isLurking:(\i).+?)/,220
replace: (m, pushNotificationButtonExpression, channel, isLurking) => `${m}if(!${isLurking}&&$self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}`221
},222
{223
match: /renderHeaderToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_MEDIA:(?=.+?(\i\.push.{0,40}channel:(\i)},"notifications"\)\)))(?<=isLurking:(\i).+?)/,224
replace: (m, pushNotificationButtonExpression, channel, isLurking) => `${m}if(!${isLurking}&&$self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}`225
},226
{227
match: /renderMobileToolbar(?:",|=)\(\)=>{.+?case \i\.\i\.GUILD_DIRECTORY:(?<=let{channel:(\i).+?)/,228
replace: (m, channel) => `${m}if($self.isHiddenChannel(${channel}))break;`229
},230
{231
match: /(?<=renderHeaderBar(?:",|=)\(\)=>{.+?hideSearch:(\i)\.isDirectory\(\))/,232
replace: (_, channel) => `||$self.isHiddenChannel(${channel})`233
},234
{235
match: /(?<=renderSidebar\(\){)/,236
replace: "if($self.isHiddenChannel(this?.props?.channel))return null;"237
},238
{239
match: /(?<=renderChat\(\){)/,240
replace: "if($self.isHiddenChannel(this?.props?.channel))return $self.HiddenChannelLockScreen(this?.props?.channel);"241
}242
]243
},244
// Avoid trying to fetch messages from hidden channels245
{246
find: 039;"MessageManager"039;,247
replacement: {248
match: /forceFetch:\i,isPreload:.+?}=\i;(?=.+?getChannel\((\i)\))/,249
replace: (m, channelId) => `${m}if($self.isHiddenChannel({channelId:${channelId}}))return;`250
}251
},252
// Patch keybind handlers so you can't accidentally jump to hidden channels253
{254
find: 039;"alt+shift+down"039;,255
replacement: {256
match: /(?<=getChannel\(\i\);return null!=(\i))(?=.{0,200}?>0\)&&\(0,\i\.\i\)\(\i\))/,257
replace: (_, channel) => `&&!$self.isHiddenChannel(${channel})`258
}259
},260
// Patch keybind handlers so you can't accidentally jump to hidden channels261
{262
find: ".APPLICATION_STORE&&null!=",263
replacement: {264
match: /getState\(\)\.channelId.+?(?=\.map\(\i=>\i\.id)/,265
replace: "$&.filter(e=>!$self.isHiddenChannel(e))"266
}267
},268
{269
find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",270
replacement: [271
{272
// Change the role permission check to CONNECT if the channel is locked273
match: /(forceRoles:.+?)(\i\.\i\(\i\.\i\.ADMINISTRATOR,\i\.\i\.VIEW_CHANNEL\))(?<=context:(\i)}.+?)/,274
replace: (_, rest, mergedPermissions, channel) => `${rest}$self.swapViewChannelWithConnectPermission(${mergedPermissions},${channel})`275
},276
{277
// Change the permissionOverwrite check to CONNECT if the channel is locked278
match: /permissionOverwrites\[.+?\i=(?<=context:(\i)}.+?)(?=(.+?)VIEW_CHANNEL)/,279
replace: (m, channel, permCheck) => `${m}!Vencord.Webpack.Common.PermissionStore.can(${CONNECT}n,${channel})?${permCheck}CONNECT):`280
},281
{282
// Include the @everyone role in the allowed roles list for Hidden Channels283
match: /getSortedRoles.+?\.filter\(\i=>(?=!)/,284
replace: m => `${m}$self.isHiddenChannel(arguments[0]?.channel)?true:`285
},286
{287
// If the @everyone role has the required permissions, make the array only contain it288
match: /forceRoles:.+?.value\(\)(?<=channel:(\i).+?)/,289
replace: (m, channel) => `${m}.reduce(...$self.makeAllowedRolesReduce(${channel}.guild_id))`290
},291
{292
// 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)293
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.+?\]}\)),)/,294
replace: (m, channel, allowedUsersAndRolesComponent) => `if($self.isHiddenChannel(${channel},true)){return${allowedUsersAndRolesComponent};}${m}`295
},296
{297
// Export the channel for the users allowed component patch298
match: /maxUsers:\d+?,users:\i(?<=channel:(\i).+?)/,299
replace: (m, channel) => `${m},shcChannel:${channel}`300
},301
{302
// Always render the component for multiple allowed users303
match: /1!==\i\.length(?=\|\|)/,304
replace: "true"305
}306
]307
},308
{309
find: 039;="interactive-text-default",overflowCountClassName:039;,310
replacement: [311
{312
// Create a variable for the channel prop313
match: /let{users:\i,maxUsers:\i,/,314
replace: "let{shcChannel}=arguments[0];$&"315
},316
{317
// Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen318
match: /\i>0(?=&&!\i&&!\i)/,319
replace: m => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)?true:${m})`320
},321
{322
// Show only the plus text without overflowed children amount323
// if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen324
match: /(?<=`\+\$\{)\i(?=\})/,325
replace: overflowTextAmount => "" +326
`$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&(${overflowTextAmount}-1)<=0?"":${overflowTextAmount}`327
}328
]329
},330
{331
find: "#{intl::CHANNEL_CALL_CURRENT_SPEAKER}",332
replacement: [333
{334
// Remove the open chat button for the HiddenChannelLockScreen335
match: /(?<=&&)\i\.push\(.{0,120}"chat-spacer"/,336
replace: "(arguments[0]?.inCall||!$self.isHiddenChannel(arguments[0]?.channel,true))&&$&"337
}338
]339
},340
{341
find: "#{intl::EMBEDDED_ACTIVITIES_DEVELOPER_ACTIVITY_SHELF_FETCH_ERROR}",342
replacement: [343
{344
// Render our HiddenChannelLockScreen component instead of the main voice channel component345
match: /renderContent\(\i\){.+?this\.renderVoiceChannelEffects.+?children:/,346
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?$self.HiddenChannelLockScreen(this?.props?.channel):"347
},348
{349
// Disable gradients for the HiddenChannelLockScreen of voice channels350
match: /renderContent\(\i\){.+?disableGradients:/,351
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)||"352
},353
{354
// Disable useless components for the HiddenChannelLockScreen of voice channels355
match: /(?:{|,)render(?!Header|ExternalHeader).{0,30}?:/g,356
replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?()=>null:"357
},358
{359
// Disable bad CSS class which mess up hidden voice channels styling360
match: /(?=\i\|\|\i!==\i\.\i\.FULL_SCREEN.{0,100}?this\._callContainerRef)/,361
replace: 039;$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?"":039;362
}363
]364
},365
{366
find: 039;"HasBeenInStageChannel"039;,367
replacement: [368
{369
// Render our HiddenChannelLockScreen component instead of the main stage channel component370
match: /screenMessage:(\i)\?.+?children:(?=!\1)(?<=let \i,{channel:(\i).+?)/,371
replace: (m, _isPopoutOpen, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):`372
},373
{374
// Disable useless components for the HiddenChannelLockScreen of stage channels375
match: /render(?:BottomLeft|BottomCenter|BottomRight|ChatToasts):\(\)=>(?<=let \i,{channel:(\i).+?)/g,376
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?null:`377
},378
{379
// Disable gradients for the HiddenChannelLockScreen of stage channels380
match: /"124px".+?disableGradients:(?<=let \i,{channel:(\i).+?)/,381
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})||`382
},383
{384
// Disable strange styles applied to the header for the HiddenChannelLockScreen of stage channels385
match: /"124px".+?style:(?<=let \i,{channel:(\i).+?)/,386
replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?void 0:`387
}388
]389
},390
{391
find: "#{intl::STAGE_FULL_MODERATOR_TITLE}",392
replacement: [393
{394
// Remove the divider and amount of users in stage channel components for the HiddenChannelLockScreen395
match: /\(0,\i\.jsx\)\(\i\.\i\.Divider.+?}\)]}\)(?=.+?:(\i)\.guild_id)/,396
replace: (m, channel) => `$self.isHiddenChannel(${channel})?null:(${m})`397
},398
{399
// Remove the open chat button for the HiddenChannelLockScreen400
match: /(?<=numRequestToSpeak:\i\}\)\}\):null,!\i&&)\(0,\i\.jsxs?\).{0,280}?iconClassName:/,401
replace: "!$self.isHiddenChannel(arguments[0]?.channel,true)&&$&"402
}403
]404
},405
{406
// Make the chat input bar channel list contain hidden channels407
find: ",queryStaticRouteChannels(",408
replacement: [409
{410
// Make the getChannels call to GuildChannelStore return hidden channels411
match: /(?<=queryChannels\(\i\){.+?getChannels\(\i)(?=\))/,412
replace: ",true"413
},414
{415
// Avoid filtering out hidden channels from the channel list416
match: /(?<=queryChannels\(\i\){.+?\)\((\i)\.type\))(?=&&!\i\.\i\.can\()/,417
replace: "&&!$self.isHiddenChannel($1)"418
}419
]420
},421
{422
find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"",423
replacement: {424
// Make mentions of hidden channels work425
match: /\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL,\i\)/,426
replace: "true"427
},428
},429
{430
find: 039;getConfig({location:"channel_mention"})039;,431
replacement: {432
// Show inside voice channel instead of trying to join them when clicking on a channel mention433
match: /(?<=getChannel\(\i\);if\(null!=(\i)).{0,200}?return void (?=\i\.default\.selectVoiceChannel)/,434
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`435
}436
},437
{438
find: 039;"GuildChannelStore"039;,439
replacement: [440
{441
// Make GuildChannelStore contain hidden channels442
match: /isChannelGated\(.+?\)(?=&&)/,443
replace: m => `${m}&&false`444
},445
{446
// Filter hidden channels from GuildChannelStore.getChannels unless told otherwise447
match: /(?<=getChannels\(\i)(\){.*?)return (.+?)}/,448
replace: (_, rest, channels) => `,shouldIncludeHidden${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden??arguments[0]==="@favorites");}`449
},450
]451
},452
{453
find: "GuildTooltip - ",454
replacement: {455
// Make GuildChannelStore.getChannels return hidden channels456
match: /(?<=getChannels\(\i)(?=\))/,457
replace: ",true"458
}459
},460
{461
find: 039;"NowPlayingViewStore"039;,462
replacement: {463
// Make active now voice states on hidden channels464
match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/,465
replace: "$1"466
}467
},468
{469
find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",470
replacement: {471
match: /(?=function (\i)\(\i\){let{channel:.{0,200}?getSortedRoles\()/,472
replace: "$self.ChannelBeginHeader=$1;"473
}474
}475
],476
477
set ChannelBeginHeader(value: any) {478
setChannelBeginHeader(value);479
},480
481
swapViewChannelWithConnectPermission(mergedPermissions: bigint, channel: Channel) {482
if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) {483
mergedPermissions &= ~PermissionsBits.VIEW_CHANNEL;484
mergedPermissions |= PermissionsBits.CONNECT;485
}486
487
return mergedPermissions;488
},489
490
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {491
try {492
if (channel == null || Object.hasOwn(channel, "channelId") && channel.channelId == null) return false;493
494
if (channel.channelId != null) channel = ChannelStore.getChannel(channel.channelId);495
if (channel == null || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;496
if (["browse", "customize", "guide"].includes(channel.id)) return false;497
498
return !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || checkConnect && !PermissionStore.can(PermissionsBits.CONNECT, channel);499
} catch (e) {500
console.error("[ViewHiddenChannels#isHiddenChannel]: ", e);501
return false;502
}503
},504
505
resolveGuildChannels(channels: Record<string | number, Array<{ channel: Channel; comparator: number; }> | string | number>, shouldIncludeHidden: boolean) {506
if (shouldIncludeHidden) return channels;507
508
const res = {};509
for (const [key, maybeObjChannels] of Object.entries(channels)) {510
if (!Array.isArray(maybeObjChannels)) {511
res[key] = maybeObjChannels;512
continue;513
}514
515
res[key] ??= [];516
517
for (const objChannel of maybeObjChannels) {518
if (isUncategorized(objChannel) || objChannel.channel.id === null || !this.isHiddenChannel(objChannel.channel)) res[key].push(objChannel);519
}520
}521
522
return res;523
},524
525
makeAllowedRolesReduce(guildId: string) {526
return [527
(prev: Array<Role>, _: Role, index: number, originalArray: Array<Role>) => {528
if (index !== 0) return prev;529
530
const everyoneRole = originalArray.find(role => role.id === guildId);531
532
if (everyoneRole) return [everyoneRole];533
return originalArray;534
},535
[] as Array<Role>536
];537
},538
539
HiddenChannelLockScreen: (channel: any) => <HiddenChannelLockScreen channel={channel} />,540
541
LockIcon: ErrorBoundary.wrap(() => (542
<svg543
className={ChannelListClasses.icon}544
height="18"545
width="20"546
viewBox="0 0 24 24"547
aria-hidden={true}548
role="img"549
>550
<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" />551
</svg>552
), { noop: true }),553
554
HiddenChannelIcon: ErrorBoundary.wrap(() => (555
<Tooltip text="Hidden Channel">556
{({ onMouseLeave, onMouseEnter }) => (557
<svg558
onMouseLeave={onMouseLeave}559
onMouseEnter={onMouseEnter}560
className={classes(ChannelListClasses.icon, cl("hidden-channel-icon"))}561
width="24"562
height="24"563
viewBox="0 0 24 24"564
aria-hidden={true}565
role="img"566
>567
<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" />568
</svg>569
)}570
</Tooltip>571
), { noop: true })572
});573