diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/showHiddenChannels.tsx | 171 |
1 files changed, 99 insertions, 72 deletions
diff --git a/src/plugins/showHiddenChannels.tsx b/src/plugins/showHiddenChannels.tsx index 1225cf1..91e4c3b 100644 --- a/src/plugins/showHiddenChannels.tsx +++ b/src/plugins/showHiddenChannels.tsx @@ -17,57 +17,56 @@ */ -import { Settings } from "@api/settings"; +import { definePluginSettings } from "@api/settings"; import { Badge } from "@components/Badge"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { proxyLazy } from "@utils/proxyLazy"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Button, ChannelStore, moment, Parser, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip } from "@webpack/common"; +import { Channel } from "discord-types/general"; const ChannelListClasses = findByPropsLazy("channelName", "subtitle", "modeMuted", "iconContainer"); +const Permissions = findByPropsLazy("VIEW_CHANNEL", "ADMINISTRATOR"); +const ChannelTypes = findByPropsLazy("GUILD_TEXT", "GUILD_FORUM"); -const VIEW_CHANNEL = 1024n; - -enum ChannelTypes { - GUILD_TEXT = 0, - GUILD_ANNOUNCEMENT = 5, - GUILD_FORUM = 15 -} - -const ChannelTypesToChannelName = { +const ChannelTypesToChannelName = proxyLazy(() => ({ [ChannelTypes.GUILD_TEXT]: "TEXT", [ChannelTypes.GUILD_ANNOUNCEMENT]: "ANNOUNCEMENT", [ChannelTypes.GUILD_FORUM]: "FORUM" -}; +})); 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], - options: { - 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 - } - }, + settings, + patches: [ { // RenderLevel defines if a channel is hidden, collapsed in category, visible, etc @@ -75,96 +74,125 @@ export default definePlugin({ // These replacements only change the necessary CannotShow's replacement: [ { - match: /(?<restOfFunction>renderLevel:(?<renderLevelExpression>\i\(this,\i\)\?\i\.Show:\i\.WouldShowIfUncollapsed).+?renderLevel:).+?,/, - replace: "$<restOfFunction>$<renderLevelExpression>," + 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: /(?<restOfFunction>activeJoinedRelevantThreads.{1,100}renderLevel:(?<RenderLevels>\i)\.Show.+?renderLevel:).+?,/, - replace: "$<restOfFunction>$<RenderLevels>.Show," + 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: /(?<restOfFunction>isChannelGatedAndVisible\(this\.record\.guild_id,this\.record\.id\).+?renderLevel:)(?<RenderLevels>\i)\.CannotShow/, - replace: "$<restOfFunction>this.category.isCollapsed?$<RenderLevels>.WouldShowIfUncollapsed:$<RenderLevels>.Show" + match: /(?<=renderLevel:(?<renderLevelExpression>\i\(this,\i\)\?\i\.Show:\i\.WouldShowIfUncollapsed).+?renderLevel:).+?(?=,)/, + replace: "$<renderLevelExpression>" }, { - match: /(?<restOfFunction>getRenderLevel=function.+?return).+?\?(?<renderLevelExpression>.+?):\i\.CannotShow}/, - replace: "$<restOfFunction> $<renderLevelExpression>}" + match: /(?<=activeJoinedRelevantThreads.+?renderLevel:.+?,threadIds:\i\(this.record.+?renderLevel:)(?<RenderLevels>\i)\..+?(?=,)/, + replace: "$<RenderLevels>.Show" + }, + { + match: /(?<=getRenderLevel=function.+?return ).+?\?(?<renderLevelExpressionWithoutPermCheck>.+?):\i\.CannotShow(?=})/, + replace: "$<renderLevelExpressionWithoutPermCheck>" } ] }, { - // inside the onMouseClick handler, we check if the channel is hidden and open the modal if it is - find: ".handleThreadsPopoutClose();", + // inside the onMouseDown handler, we check if the channel is hidden and open the modal if it is + find: "VoiceChannel.renderPopout: There must always be something to render", + replacement: [ + { + match: /(?=(?<this>\i)\.handleThreadsPopoutClose\(\))/, + replace: "if($self.isHiddenChannel($<this>.props.channel)&&arguments[0].button===0){" + + "$self.onHiddenChannelSelected($<this>.props.channel);" + + "return;" + + "}" + }, + // 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: { - match: /(?<this>\i)\.handleThreadsPopoutClose\(\);/, - replace: "if(arguments[0].button===0&&$self.channelSelected($<this>?.props?.channel))return;$&" + // 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.plugins.ShowHiddenChannels.hideUnreads === true, + predicate: () => settings.store.hideUnreads === true, replacement: [{ // Hide unreads - match: /(?<restOfFunction>\i\.connected,)(?<hasUnread>\i)=(?<props>\i).unread/, - replace: "$<restOfFunction>$<hasUnread>=$self.isHiddenChannel($<props>.channel)?false:$<props>.unread" + match: /(?<=\i\.connected,\i=)(?=(?<props>\i)\.unread)/, + replace: "$self.isHiddenChannel($<props>.channel)?false:" }] }, { - find: ".Messages.CHANNEL_TOOLTIP_DIRECTORY", - predicate: () => Settings.plugins.ShowHiddenChannels.showMode === ShowMode.LockIcon, - replacement: { - // Lock Icon - match: /switch\((?<channel>\i)\.type\).{1,30}\.GUILD_ANNOUNCEMENT.{1,30}\(0,\i\.\i\)\(\i\)/, - replace: "if($self.isHiddenChannel($<channel>))return $self.LockIcon;$&" - } - }, - { find: ".UNREAD_HIGHLIGHT", - predicate: () => Settings.plugins.ShowHiddenChannels.showMode === ShowMode.HiddenIconWithMutedStyle, + predicate: () => settings.store.showMode === ShowMode.HiddenIconWithMutedStyle, replacement: [ // Make the channel appear as muted if it's hidden { - match: /(?<restOfFunction>\i\.name,)(?<isMuted>\i)=(?<props>\i).muted/, - replace: "$<restOfFunction>$<isMuted>=$self.isHiddenChannel($<props>.channel)?true:$<props>.muted" + 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:(?<channel>\i),.+?\.channelName.+?\.children.+?:null/, - replace: "$&,$self.isHiddenChannel($<channel>)?$self.HiddenChannelIcon():null" + 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: /(?<restOfFunction>.wrapper:\i\(\).notInteractive,)(?<secondRestOfFunction>.+?)(?<isMutedClassExpression>(?<isMuted>\i)\?\i\.MUTED:)/, - replace: "$<restOfFunction>$<isMutedClassExpression>\"\",$<secondRestOfFunction>$<isMuted>?\"\":" + 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: /(?<restOfFunction>return null!=(?<channel>\i))(?<secondRestOfFunction>&&null!=\i\.getGuildId\(\).{1,120}hasRelevantUnread\(\i\)\))/, - replace: "$<restOfFunction>&&!$self.isHiddenChannel($<channel>)$<secondRestOfFunction>" + match: /(?<=return null!=(?<channel>\i))(?=.{1,130}hasRelevantUnread\(\i\))/, + replace: "&&!$self.isHiddenChannel($<channel>)" } - }, + } ], - isHiddenChannel(channel) { + 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(VIEW_CHANNEL, channel); + return !PermissionStore.can(Permissions.VIEW_CHANNEL, channel); }, - channelSelected(channel) { - if (!channel) return false; - - const isHidden = this.isHiddenChannel(channel); - + onHiddenChannelSelected(channel: Channel) { // Check for type, otherwise it would attempt to show the modal for stage channels - if ([ChannelTypes.GUILD_TEXT, ChannelTypes.GUILD_ANNOUNCEMENT, ChannelTypes.GUILD_FORUM].includes(channel.type) && isHidden) { + if ([ChannelTypes.GUILD_TEXT, ChannelTypes.GUILD_ANNOUNCEMENT, ChannelTypes.GUILD_FORUM].includes(channel.type)) { openModal(modalProps => ( <ModalRoot size={ModalSize.SMALL} {...modalProps}> <ModalHeader> @@ -174,7 +202,7 @@ export default definePlugin({ {channel.isNSFW() && <Badge text="NSFW" color="var(--status-danger)" />} </Flex> </ModalHeader> - <ModalContent style={{ marginBottom: 10, marginTop: 10, marginRight: 8, marginLeft: 8 }}> + <ModalContent style={{ margin: "10px 8px" }}> <Text variant="text-md/normal">You don't have permission to view {channel.type === ChannelTypes.GUILD_FORUM ? "posts" : "messages"} in this channel.</Text> {(channel.topic ?? "").length > 0 && ( <> @@ -211,7 +239,6 @@ export default definePlugin({ </ModalRoot> )); } - return isHidden; }, LockIcon: () => ( |