mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-17 18:37:04 -04:00
ShowHiddenChannels: New screen for showing hidden channels (#460)
Co-authored-by: Ven <vendicated@riseup.net>
This commit is contained in:
parent
8f4e8d0a9b
commit
369d179bbf
6 changed files with 366 additions and 77 deletions
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { LazyComponent } from "@utils/misc";
|
||||
import { proxyLazy } from "@utils/proxyLazy";
|
||||
import { formatDuration } from "@utils/text";
|
||||
import { find, findByCode, findByPropsLazy, findLazy } from "@webpack";
|
||||
import { moment, Parser, SnowflakeUtils, Text, Timestamp, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
enum SortOrderTypesTyping {
|
||||
LATEST_ACTIVITY = 0,
|
||||
CREATION_DATE = 1
|
||||
}
|
||||
|
||||
enum ForumLayoutTypesTyping {
|
||||
DEFAULT = 0,
|
||||
LIST = 1,
|
||||
GRID = 2
|
||||
}
|
||||
|
||||
interface DefaultReaction {
|
||||
emojiId: string | null;
|
||||
emojiName: string | null;
|
||||
}
|
||||
|
||||
interface Tag {
|
||||
id: string;
|
||||
name: string;
|
||||
emojiId: string | null;
|
||||
emojiName: string | null;
|
||||
moderated: boolean;
|
||||
}
|
||||
|
||||
interface ExtendedChannel extends Channel {
|
||||
defaultThreadRateLimitPerUser?: number;
|
||||
defaultSortOrder?: SortOrderTypesTyping | null;
|
||||
defaultForumLayout?: ForumLayoutTypesTyping;
|
||||
defaultReactionEmoji?: DefaultReaction | null;
|
||||
availableTags?: Array<Tag>;
|
||||
}
|
||||
|
||||
const ChatClasses = findByPropsLazy("chat", "chatContent");
|
||||
const TagClasses = findLazy(m => typeof m.tags === "string" && Object.entries(m).length === 1); // Object exported with a single key called tags
|
||||
const ChannelTypes = findByPropsLazy("GUILD_TEXT", "GUILD_FORUM");
|
||||
const SortOrderTypes = findLazy(m => typeof m.LATEST_ACTIVITY === "number");
|
||||
const ForumLayoutTypes = findLazy(m => typeof m.LIST === "number");
|
||||
const ChannelFlags = findLazy(m => typeof m.REQUIRE_TAG === "number");
|
||||
const TagComponent = LazyComponent(() => find(m => {
|
||||
if (typeof m !== "function") return false;
|
||||
|
||||
const code = Function.prototype.toString.call(m);
|
||||
// Get the component which doesn't include increasedActivity logic
|
||||
return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill");
|
||||
}));
|
||||
const EmojiComponent = LazyComponent(() => findByCode('.jumboable?"jumbo":"default"'));
|
||||
|
||||
const ChannelTypesToChannelNames = proxyLazy(() => ({
|
||||
[ChannelTypes.GUILD_TEXT]: "text",
|
||||
[ChannelTypes.GUILD_ANNOUNCEMENT]: "announcement",
|
||||
[ChannelTypes.GUILD_FORUM]: "forum"
|
||||
}));
|
||||
|
||||
const SortOrderTypesToNames = proxyLazy(() => ({
|
||||
[SortOrderTypes.LATEST_ACTIVITY]: "Latest activity",
|
||||
[SortOrderTypes.CREATION_DATE]: "Creation date"
|
||||
}));
|
||||
|
||||
const ForumLayoutTypesToNames = proxyLazy(() => ({
|
||||
[ForumLayoutTypes.DEFAULT]: "Not set",
|
||||
[ForumLayoutTypes.LIST]: "List view",
|
||||
[ForumLayoutTypes.GRID]: "Gallery view"
|
||||
}));
|
||||
|
||||
// Icon from the modal when clicking a message link you don't have access to view
|
||||
const HiddenChannelLogo = "/assets/433e3ec4319a9d11b0cbe39342614982.svg";
|
||||
|
||||
function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||
const {
|
||||
type,
|
||||
topic,
|
||||
lastMessageId,
|
||||
defaultForumLayout,
|
||||
lastPinTimestamp,
|
||||
defaultAutoArchiveDuration,
|
||||
availableTags,
|
||||
id: channelId,
|
||||
rateLimitPerUser,
|
||||
defaultThreadRateLimitPerUser,
|
||||
defaultSortOrder,
|
||||
defaultReactionEmoji
|
||||
} = channel;
|
||||
|
||||
return (
|
||||
<div className={ChatClasses.chat + " " + "shc-lock-screen-container"}>
|
||||
<img className="shc-lock-screen-logo" src={HiddenChannelLogo} />
|
||||
|
||||
<div className="shc-lock-screen-heading-container">
|
||||
<Text variant="heading-xxl/bold">This is a hidden {ChannelTypesToChannelNames[type]} channel.</Text>
|
||||
{channel.isNSFW() &&
|
||||
<Tooltip text="NSFW">
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<svg
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseEnter={onMouseEnter}
|
||||
className="shc-lock-screen-heading-nsfw-icon"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 48 48"
|
||||
aria-hidden={true}
|
||||
role="img"
|
||||
>
|
||||
<path d="M.7 43.05 24 2.85l23.3 40.2Zm23.55-6.25q.75 0 1.275-.525.525-.525.525-1.275 0-.75-.525-1.3t-1.275-.55q-.8 0-1.325.55-.525.55-.525 1.3t.55 1.275q.55.525 1.3.525Zm-1.85-6.1h3.65V19.4H22.4Z" />
|
||||
</svg>
|
||||
)}
|
||||
</Tooltip>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Text variant="text-lg/normal">
|
||||
You can not see the {channel.isForumChannel() ? "posts" : "messages"} of this channel.
|
||||
{channel.isForumChannel() && topic && topic.length > 0 && "However you may see its guidelines:"}
|
||||
</Text >
|
||||
|
||||
{channel.isForumChannel() && topic && topic.length > 0 && (
|
||||
<div className="shc-lock-screen-topic-container">
|
||||
{Parser.parseTopic(topic, false, { channelId })}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{lastMessageId &&
|
||||
<Text variant="text-md/normal">
|
||||
Last {channel.isForumChannel() ? "post" : "message"} created:
|
||||
<Timestamp timestamp={moment(SnowflakeUtils.extractTimestamp(lastMessageId))} />
|
||||
</Text>
|
||||
}
|
||||
|
||||
{lastPinTimestamp &&
|
||||
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={moment(lastPinTimestamp)} /></Text>
|
||||
}
|
||||
{(rateLimitPerUser ?? 0) > 0 &&
|
||||
<Text variant="text-md/normal">Slowmode: {formatDuration(rateLimitPerUser! * 1000)}</Text>
|
||||
}
|
||||
{(defaultThreadRateLimitPerUser ?? 0) > 0 &&
|
||||
<Text variant="text-md/normal">
|
||||
Default thread slowmode: {formatDuration(defaultThreadRateLimitPerUser! * 1000)}
|
||||
</Text>
|
||||
}
|
||||
{(defaultAutoArchiveDuration ?? 0) > 0 &&
|
||||
<Text variant="text-md/normal">
|
||||
Default inactivity duration before archiving {channel.isForumChannel() ? "posts" : "threads"}:
|
||||
{formatDuration(defaultAutoArchiveDuration! * 1000 * 60)}
|
||||
</Text>
|
||||
}
|
||||
{defaultForumLayout != null &&
|
||||
<Text variant="text-md/normal">Default layout: {ForumLayoutTypesToNames[defaultForumLayout]}</Text>
|
||||
}
|
||||
{defaultSortOrder != null &&
|
||||
<Text variant="text-md/normal">Default sort order: {SortOrderTypesToNames[defaultSortOrder]}</Text>
|
||||
}
|
||||
{defaultReactionEmoji != null &&
|
||||
<div className="shc-lock-screen-default-emoji-container">
|
||||
<Text variant="text-md/normal">Default reaction emoji:</Text>
|
||||
<EmojiComponent node={{
|
||||
type: defaultReactionEmoji.emojiName ? "emoji" : "customEmoji",
|
||||
name: defaultReactionEmoji.emojiName ?? "",
|
||||
emojiId: defaultReactionEmoji.emojiId
|
||||
}} />
|
||||
</div>
|
||||
}
|
||||
{channel.hasFlag(ChannelFlags.REQUIRE_TAG) &&
|
||||
<Text variant="text-md/normal">Posts on this forum require a tag to be set.</Text>
|
||||
}
|
||||
{availableTags && availableTags.length > 0 &&
|
||||
<div className="shc-lock-screen-tags-container">
|
||||
<Text variant="text-lg/bold">Available tags:</Text>
|
||||
<div className={TagClasses.tags}>
|
||||
{availableTags.map(tag => <TagComponent tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorBoundary.wrap(HiddenChannelLockScreen);
|
274
src/plugins/showHiddenChannels/index.tsx
Normal file
274
src/plugins/showHiddenChannels/index.tsx
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
|
||||
import { definePluginSettings } from "@api/settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { ChannelStore, PermissionStore, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen";
|
||||
|
||||
const ChannelListClasses = findByPropsLazy("channelName", "subtitle", "modeMuted", "iconContainer");
|
||||
const Permissions = findLazy(m => typeof m.VIEW_CHANNEL === "bigint");
|
||||
|
||||
enum ShowMode {
|
||||
LockIcon,
|
||||
HiddenIconWithMutedStyle
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
hideUnreads: {
|
||||
description: "Hide Unreads",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
restartNeeded: true
|
||||
},
|
||||
showMode: {
|
||||
description: "The mode used to display hidden channels.",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{ label: "Plain style with Lock Icon instead", value: ShowMode.LockIcon, default: true },
|
||||
{ label: "Muted style with hidden eye icon on the right", value: ShowMode.HiddenIconWithMutedStyle },
|
||||
],
|
||||
restartNeeded: true
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "ShowHiddenChannels",
|
||||
description: "Show channels that you do not have access to view.",
|
||||
authors: [Devs.BigDuck, Devs.AverageReactEnjoyer, Devs.D3SOX, Devs.Ven, Devs.Nuckyz, Devs.Nickyux, Devs.dzshn],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
// RenderLevel defines if a channel is hidden, collapsed in category, visible, etc
|
||||
find: ".CannotShow",
|
||||
// These replacements only change the necessary CannotShow's
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=isChannelGatedAndVisible\(this\.record\.guild_id,this\.record\.id\).+?renderLevel:)(?<RenderLevels>\i)\..+?(?=,)/,
|
||||
replace: "this.category.isCollapsed?$<RenderLevels>.WouldShowIfUncollapsed:$<RenderLevels>.Show"
|
||||
},
|
||||
// Move isChannelGatedAndVisible renderLevel logic to the bottom to not show hidden channels in case they are muted
|
||||
{
|
||||
match: /(?<=(?<permissionCheck>if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{)if\(this\.id===\i\).+?};)(?<isChannelGatedAndVisibleCondition>if\(!\i\.\i\.isChannelGatedAndVisible\(.+?})(?<restOfFunction>.+?)(?=return{renderLevel:\i\.Show.{1,40}return \i)/,
|
||||
replace: "$<restOfFunction>$<permissionCheck>$<isChannelGatedAndVisibleCondition>}"
|
||||
},
|
||||
{
|
||||
match: /(?<=renderLevel:(?<renderLevelExpression>\i\(this,\i\)\?\i\.Show:\i\.WouldShowIfUncollapsed).+?renderLevel:).+?(?=,)/,
|
||||
replace: "$<renderLevelExpression>"
|
||||
},
|
||||
{
|
||||
match: /(?<=activeJoinedRelevantThreads.+?renderLevel:.+?,threadIds:\i\(this.record.+?renderLevel:)(?<RenderLevels>\i)\..+?(?=,)/,
|
||||
replace: "$<RenderLevels>.Show"
|
||||
},
|
||||
{
|
||||
match: /(?<=getRenderLevel=function.+?return ).+?\?(?<renderLevelExpressionWithoutPermCheck>.+?):\i\.CannotShow(?=})/,
|
||||
replace: "$<renderLevelExpressionWithoutPermCheck>"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "VoiceChannel.renderPopout: There must always be something to render",
|
||||
replacement: [
|
||||
// Do nothing when trying to join a voice channel if the channel is hidden
|
||||
{
|
||||
match: /(?<=handleClick=function\(\){)(?=.{1,80}(?<this>\i)\.handleVoiceConnect\(\))/,
|
||||
replace: "if($self.isHiddenChannel($<this>.props.channel))return;"
|
||||
},
|
||||
// Render null instead of the buttons if the channel is hidden
|
||||
...[
|
||||
"renderEditButton",
|
||||
"renderInviteButton",
|
||||
"renderOpenChatButton"
|
||||
].map(func => ({
|
||||
match: new RegExp(`(?<=\\i\\.${func}=function\\(\\){)`, "g"), // Global because Discord has multiple declarations of the same functions
|
||||
replace: "if($self.isHiddenChannel(this.props.channel))return null;"
|
||||
}))
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".Messages.CHANNEL_TOOLTIP_DIRECTORY",
|
||||
predicate: () => settings.store.showMode === ShowMode.LockIcon,
|
||||
replacement: {
|
||||
// Lock Icon
|
||||
match: /(?=switch\((?<channel>\i)\.type\).{1,30}\.GUILD_ANNOUNCEMENT.{1,30}\(0,\i\.\i\))/,
|
||||
replace: "if($self.isHiddenChannel($<channel>))return $self.LockIcon;"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".UNREAD_HIGHLIGHT",
|
||||
predicate: () => settings.store.hideUnreads === true,
|
||||
replacement: [{
|
||||
// Hide unreads
|
||||
match: /(?<=\i\.connected,\i=)(?=(?<props>\i)\.unread)/,
|
||||
replace: "$self.isHiddenChannel($<props>.channel)?false:"
|
||||
}]
|
||||
},
|
||||
{
|
||||
find: ".UNREAD_HIGHLIGHT",
|
||||
predicate: () => settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
||||
replacement: [
|
||||
// Make the channel appear as muted if it's hidden
|
||||
{
|
||||
match: /(?<=\i\.name,\i=)(?=(?<props>\i)\.muted)/,
|
||||
replace: "$self.isHiddenChannel($<props>.channel)?true:"
|
||||
},
|
||||
// Add the hidden eye icon if the channel is hidden
|
||||
{
|
||||
match: /(?<=(?<channel>\i)=\i\.channel,.+?\(\)\.children.+?:null)/,
|
||||
replace: ",$self.isHiddenChannel($<channel>)?$self.HiddenChannelIcon():null"
|
||||
},
|
||||
// Make voice channels also appear as muted if they are muted
|
||||
{
|
||||
match: /(?<=\i\(\)\.wrapper:\i\(\)\.notInteractive,)(?<otherClasses>.+?)(?<mutedClassExpression>(?<isMuted>\i)\?\i\.MUTED)/,
|
||||
replace: "$<mutedClassExpression>:\"\",$<otherClasses>$<isMuted>?\"\""
|
||||
}
|
||||
]
|
||||
},
|
||||
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden
|
||||
{
|
||||
find: ".UNREAD_HIGHLIGHT",
|
||||
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
||||
replacement: {
|
||||
match: /(?<=(?<channel>\i)=\i\.channel,.+?\.LOCKED:\i)/,
|
||||
replace: "&&!($self.settings.store.hideUnreads===false&&$self.isHiddenChannel($<channel>))"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Hide New unreads box for hidden channels
|
||||
find: '.displayName="ChannelListUnreadsStore"',
|
||||
replacement: {
|
||||
match: /(?<=return null!=(?<channel>\i))(?=.{1,130}hasRelevantUnread\(\i\))/,
|
||||
replace: "&&!$self.isHiddenChannel($<channel>)"
|
||||
}
|
||||
},
|
||||
// Only render the channel header and buttons that work when transitioning to a hidden channel
|
||||
{
|
||||
find: "Missing channel in Channel.renderHeaderToolbar",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=renderHeaderToolbar=function.+?case \i\.\i\.GUILD_TEXT:)(?=.+?;(?<pushNotificationButtonExpression>.+?{channel:(?<channel>\i)},"notifications"\)\);))/,
|
||||
replace: "if($self.isHiddenChannel($<channel>)){$<pushNotificationButtonExpression>break;}"
|
||||
},
|
||||
{
|
||||
match: /(?<=renderHeaderToolbar=function.+?case \i\.\i\.GUILD_FORUM:if\(!\i\){)(?=.+?;(?<pushNotificationButtonExpression>.+?{channel:(?<channel>\i)},"notifications"\)\)))/,
|
||||
replace: "if($self.isHiddenChannel($<channel>)){$<pushNotificationButtonExpression>;break;}"
|
||||
},
|
||||
{
|
||||
match: /(?<=(?<this>\i)\.renderMobileToolbar=function.+?case \i\.\i\.GUILD_FORUM:)/,
|
||||
replace: "if($self.isHiddenChannel($<this>.props.channel))break;"
|
||||
},
|
||||
{
|
||||
match: /(?<=renderHeaderBar=function.+?hideSearch:(?<channel>\i)\.isDirectory\(\))/,
|
||||
replace: "||$self.isHiddenChannel($<channel>)"
|
||||
},
|
||||
{
|
||||
match: /(?<=renderSidebar=function\(\){)/,
|
||||
replace: "if($self.isHiddenChannel(this.props.channel))return null;"
|
||||
},
|
||||
{
|
||||
match: /(?<=renderChat=function\(\){)/,
|
||||
replace: "if($self.isHiddenChannel(this.props.channel))return $self.HiddenChannelLockScreen(this.props.channel);"
|
||||
},
|
||||
]
|
||||
},
|
||||
// Avoid trying to fetch messages from hidden channels
|
||||
{
|
||||
find: '"MessageManager"',
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=if\(null!=(?<channelId>\i)\).{1,100}"Skipping fetch because channelId is a static route".{1,10}else{)/,
|
||||
replace: "if($self.isHiddenChannel({channelId:$<channelId>}))return;"
|
||||
},
|
||||
]
|
||||
},
|
||||
// Patch keybind handlers so you can't accidentally jump to hidden channels
|
||||
{
|
||||
find: '"alt+shift+down"',
|
||||
replacement: {
|
||||
match: /(?<=getChannel\(\i\);return null!=(?<channel>\i))(?=.{1,130}hasRelevantUnread\(\i\))/,
|
||||
replace: "&&!$self.isHiddenChannel($<channel>)"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: '"alt+down"',
|
||||
replacement: {
|
||||
match: /(?<=getState\(\)\.channelId.{1,30}\(0,\i\.\i\)\(\i\))(?=\.map\()/,
|
||||
replace: ".filter(ch=>!$self.isHiddenChannel(ch))"
|
||||
}
|
||||
},
|
||||
// Export the emoji component used on the lock screen
|
||||
{
|
||||
find: 'jumboable?"jumbo":"default"',
|
||||
replacement: {
|
||||
match: /(?<=\i:\(\)=>\i)(?=}.+?(?<component>\i)=function.{1,20}node,\i=\i.isInteracting)/,
|
||||
replace: ",hc1:()=>$<component>" // Blame Ven length check for the small name :pensive_cry:
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
isHiddenChannel(channel: Channel & { channelId?: string; }) {
|
||||
if (!channel) return false;
|
||||
|
||||
if (channel.channelId) channel = ChannelStore.getChannel(channel.channelId);
|
||||
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;
|
||||
|
||||
return !PermissionStore.can(Permissions.VIEW_CHANNEL, channel);
|
||||
},
|
||||
|
||||
HiddenChannelLockScreen: (channel: any) => <HiddenChannelLockScreen channel={channel} />,
|
||||
|
||||
LockIcon: () => (
|
||||
<svg
|
||||
className={ChannelListClasses.icon}
|
||||
height="18"
|
||||
width="20"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden={true}
|
||||
role="img"
|
||||
>
|
||||
<path className="shc-evenodd-fill-current-color " d="M.7 43.05 24 2.85l23.3 40.2Zm23.55-6.25q.75 0 1.275-.525.525-.525.525-1.275 0-.75-.525-1.3t-1.275-.55q-.8 0-1.325.55-.525.55-.525 1.3t.55 1.275q.55.525 1.3.525Zm-1.85-6.1h3.65V19.4H22.4Z" />
|
||||
</svg>
|
||||
),
|
||||
|
||||
HiddenChannelIcon: ErrorBoundary.wrap(() => (
|
||||
<Tooltip text="Hidden Channel">
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<svg
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseEnter={onMouseEnter}
|
||||
className={ChannelListClasses.icon + " " + "shc-hidden-channel-icon"}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden={true}
|
||||
role="img"
|
||||
>
|
||||
<path className="shc-evenodd-fill-current-color " 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" />
|
||||
</svg>
|
||||
)}
|
||||
</Tooltip>
|
||||
), { noop: true })
|
||||
});
|
78
src/plugins/showHiddenChannels/style.css
Normal file
78
src/plugins/showHiddenChannels/style.css
Normal file
|
@ -0,0 +1,78 @@
|
|||
.shc-lock-screen-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shc-lock-screen-container > * {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.shc-lock-screen-logo {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.shc-lock-screen-heading-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shc-lock-screen-heading-container > * {
|
||||
margin: inherit;
|
||||
}
|
||||
|
||||
.shc-lock-screen-heading-nsfw-icon > path {
|
||||
fill: var(--text-normal);
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.shc-lock-screen-topic-container {
|
||||
color: var(--text-normal);
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
max-width: 70vw;
|
||||
}
|
||||
|
||||
.shc-lock-screen-tags-container {
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
max-width: 70vw;
|
||||
}
|
||||
|
||||
.shc-lock-screen-tags-container > * {
|
||||
margin: inherit;
|
||||
}
|
||||
|
||||
.shc-lock-screen-tags-container > [class^="tags"] {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.shc-evenodd-fill-current-color {
|
||||
fill-rule: evenodd;
|
||||
fill: currentcolor;
|
||||
}
|
||||
|
||||
.shc-hidden-channel-icon {
|
||||
margin-left: 6px;
|
||||
z-index: 0;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.shc-lock-screen-default-emoji-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shc-lock-screen-default-emoji-container > [class^="emojiContainer"] {
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 3px 4px;
|
||||
margin-left: 5px;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue