diff --git a/src/equicordplugins/channelBadges/index.ts b/src/equicordplugins/channelBadges/index.ts deleted file mode 100644 index 3c0f5838a..000000000 --- a/src/equicordplugins/channelBadges/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import "./style.css"; - -import { EquicordDevs } from "@utils/constants"; -import { getCurrentGuild } from "@utils/discord"; -import definePlugin from "@utils/types"; -import { Channel, Guild } from "@vencord/discord-types"; -import { ChannelStore, SelectedGuildStore } from "@webpack/common"; - -import { isEnabled, returnChannelBadge, settings } from "./settings"; - -let observer: MutationObserver | null = null; -let currentGuild: Guild | undefined | null = null; - -function addBadgesToChannel(element: HTMLElement, channelId: string) { - const parentContainer: HTMLElement | null = element.querySelector('[class*="linkTop"]'); - if (!parentContainer) return; - - const channel: Channel | undefined = ChannelStore.getChannel(channelId); - if (!channel || !isEnabled(channel.type)) return; - - const { type, nsfw, threadMetadata } = channel; - const isPrivate = channel.isPrivate() || threadMetadata?.locked || channel.isArchivedThread(); - const isNSFW = nsfw || channel.isNSFW(); - - let badgeContainer: HTMLElement | null = parentContainer.querySelector(".badge-container"); - if (!badgeContainer) { - badgeContainer = document.createElement("div"); - badgeContainer.classList.add("badge-container"); - parentContainer.appendChild(badgeContainer); - } - - const badgeConditions = [ - { id: 6101, condition: isPrivate, title: "This channel is locked." }, - { id: 6100, condition: isNSFW, title: "This channel is marked as NSFW." }, - { id: 6102, condition: currentGuild?.rulesChannelId === channel.id, title: "This channel is the server rules channel." }, - ]; - - badgeConditions.forEach(({ id, condition, title }) => { - if (condition && isEnabled(id)) addBadge(badgeContainer!, id, title); - }); - - addBadge(badgeContainer!, type, returnChannelBadge(type).label); -} - -function addBadge(container: HTMLElement, id: number, title: string) { - const { css, label, color } = returnChannelBadge(id); - const badge = document.createElement("div"); - badge.classList.add("channel-badge", `channel-badge-${css}`); - badge.textContent = label; - badge.title = title; - - if (color && color !== "") { - badge.style.backgroundColor = color; - } - - container.appendChild(badge); -} - -function deleteAllBadges() { - document.querySelectorAll(".channel-badge").forEach(badge => badge.remove()); - document.querySelectorAll('[data-list-item-id^="channels___"][data-scanned]').forEach(element => { - element.removeAttribute("data-scanned"); - }); -} - -export function reloadBadges() { - deleteAllBadges(); - document.querySelectorAll('[data-list-item-id^="channels___"]').forEach(element => { - const channelId = element.getAttribute("data-list-item-id")?.split("___")[1]; - if (channelId && /^\d+$/.test(channelId)) { - addBadgesToChannel(element as HTMLElement, channelId); - element.setAttribute("data-scanned", "true"); - } - }); -} - -function observeDomChanges() { - if (observer) observer.disconnect(); - - const handleMutations = (mutations: MutationRecord[]) => { - const addedElements = new Set(); - mutations.forEach(mutation => { - mutation.addedNodes.forEach(node => { - if (node.nodeType === Node.ELEMENT_NODE) { - const element = node as Element; - element.querySelectorAll('[data-list-item-id^="channels___"]:not([data-scanned])').forEach(child => { - addedElements.add(child); - }); - } - }); - }); - - addedElements.forEach(element => { - const channelId = element.getAttribute("data-list-item-id")?.split("___")[1]; - if (channelId && /^\d+$/.test(channelId)) { - addBadgesToChannel(element as HTMLElement, channelId); - element.setAttribute("data-scanned", "true"); - } - }); - }; - - observer = new MutationObserver(handleMutations); - observer.observe(document.body, { childList: true, subtree: true }); -} - -function onGuildChange() { - const newGuild: Guild | undefined | null = getCurrentGuild(); - if (newGuild !== currentGuild) { - currentGuild = newGuild; - } -} - -export default definePlugin({ - name: "ChannelBadges", - description: "Adds badges to channels based on their type", - authors: [EquicordDevs.creations], - settings, - - async start() { - currentGuild = getCurrentGuild(); - observeDomChanges(); - reloadBadges(); - SelectedGuildStore.addChangeListener(onGuildChange); - }, - - stop() { - if (observer) { - observer.disconnect(); - observer = null; - } - deleteAllBadges(); - SelectedGuildStore.removeChangeListener(onGuildChange); - } -}); diff --git a/src/equicordplugins/channelBadges/index.tsx b/src/equicordplugins/channelBadges/index.tsx new file mode 100644 index 000000000..b1a2ddb28 --- /dev/null +++ b/src/equicordplugins/channelBadges/index.tsx @@ -0,0 +1,91 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { Channel } from "@vencord/discord-types"; +import { GuildStore, React, SelectedGuildStore } from "@webpack/common"; +import { JSX } from "react"; + +import { isEnabled, returnChannelBadge, settings } from "./settings"; + +function renderBadge(id: number, title: string) { + const { css, label, color } = returnChannelBadge(id); + + return ( +
+ {label} +
+ ); +} + +export default definePlugin({ + name: "ChannelBadges", + description: "Adds badges to channels based on their type", + authors: [EquicordDevs.creations], + settings, + patches: [ + // TY TypingIndicator + // Normal Channels + { + find: "UNREAD_IMPORTANT:", + replacement: { + match: /\.Children\.count.+?:null(?<=,channel:(\i).+?)/, + replace: "$&,$self.renderChannelBadges($1)" + } + }, + // Threads + { + find: "spineWithGuildIcon]:", + replacement: { + match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/, + replace: "$&,$self.renderChannelBadges($1)" + } + } + ], + renderChannelBadges(channel: Channel) { + if (!channel || !isEnabled(channel.type)) return null; + + const { type, nsfw, threadMetadata } = channel; + const isPrivate = channel.isPrivate() || threadMetadata?.locked || channel.isArchivedThread(); + const isNSFW = nsfw || channel.isNSFW(); + + const selectedGuildId = SelectedGuildStore.getGuildId(); + const guild = selectedGuildId ? GuildStore.getGuild(selectedGuildId) : null; + + const badgeConditions = [ + { id: 6101, condition: isPrivate, title: "This channel is locked." }, + { id: 6100, condition: isNSFW, title: "This channel is marked as NSFW." }, + { id: 6102, condition: guild?.rulesChannelId === channel.id, title: "This channel is the server rules channel." }, + ]; + + let badges: JSX.Element[] = []; + + if (settings.store.oneBadgePerChannel) { + const first = badgeConditions.find(({ id, condition }) => condition && isEnabled(id)); + if (first) { + badges.push(renderBadge(first.id, first.title)); + } else { + badges.push(renderBadge(type, returnChannelBadge(type).label)); + } + } else { + badges = badgeConditions + .filter(({ id, condition }) => condition && isEnabled(id)) + .map(({ id, title }) => renderBadge(id, title)); + + badges.push(renderBadge(type, returnChannelBadge(type).label)); + } + + return
{badges}
; + } +}); diff --git a/src/equicordplugins/channelBadges/settings.tsx b/src/equicordplugins/channelBadges/settings.tsx index c0283bd53..baa38992d 100644 --- a/src/equicordplugins/channelBadges/settings.tsx +++ b/src/equicordplugins/channelBadges/settings.tsx @@ -7,276 +7,273 @@ import { definePluginSettings } from "@api/Settings"; import { OptionType } from "@utils/types"; -import { reloadBadges } from "./index"; - -const settings = definePluginSettings({ +export const settings = definePluginSettings({ oneBadgePerChannel: { type: OptionType.BOOLEAN, default: false, description: "Show only one badge per channel", - onChange: reloadBadges, + restartNeeded: true, }, showTextBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Text badge", - onChange: reloadBadges, + restartNeeded: true, }, showVoiceBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Voice badge", - onChange: reloadBadges, + restartNeeded: true, }, showCategoryBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Category badge", - onChange: reloadBadges, + restartNeeded: true, }, showDirectoryBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Directory badge", - onChange: reloadBadges, + restartNeeded: true, }, showAnnouncementThreadBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Announcement Thread badge", - onChange: reloadBadges, + restartNeeded: true, }, showPublicThreadBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Public Thread badge", - onChange: reloadBadges, + restartNeeded: true, }, showPrivateThreadBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Private Thread badge", - onChange: reloadBadges, + restartNeeded: true, }, showStageBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Stage badge", - onChange: reloadBadges, + restartNeeded: true, }, showAnnouncementBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Announcement badge", - onChange: reloadBadges, + restartNeeded: true, }, showForumBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Forum badge", - onChange: reloadBadges, + restartNeeded: true, }, showMediaBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Media badge", - onChange: reloadBadges, + restartNeeded: true, }, showNSFWBadge: { type: OptionType.BOOLEAN, default: true, description: "Show NSFW badge", - onChange: reloadBadges, + restartNeeded: true, }, showLockedBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Locked badge", - onChange: reloadBadges, + restartNeeded: true, }, showRulesBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Rules badge", - onChange: reloadBadges, + restartNeeded: true, }, showUnknownBadge: { type: OptionType.BOOLEAN, default: true, description: "Show Unknown badge", - onChange: reloadBadges, + restartNeeded: true, }, textBadgeLabel: { type: OptionType.STRING, default: "Text", description: "Text badge label", - onChange: reloadBadges, + restartNeeded: true, }, voiceBadgeLabel: { type: OptionType.STRING, default: "Voice", description: "Voice badge label", - onChange: reloadBadges, + restartNeeded: true, }, categoryBadgeLabel: { type: OptionType.STRING, default: "Category", description: "Category badge label", - onChange: reloadBadges, + restartNeeded: true, }, announcementBadgeLabel: { type: OptionType.STRING, default: "News", description: "Announcement badge label", - onChange: reloadBadges, + restartNeeded: true, }, announcementThreadBadgeLabel: { type: OptionType.STRING, default: "News Thread", description: "Announcement Thread badge label", - onChange: reloadBadges, + restartNeeded: true, }, publicThreadBadgeLabel: { type: OptionType.STRING, default: "Thread", description: "Public Thread badge label", - onChange: reloadBadges, + restartNeeded: true, }, privateThreadBadgeLabel: { type: OptionType.STRING, default: "Private Thread", description: "Private Thread badge label", - onChange: reloadBadges, + restartNeeded: true, }, stageBadgeLabel: { type: OptionType.STRING, default: "Stage", description: "Stage badge label", - onChange: reloadBadges, + restartNeeded: true, }, directoryBadgeLabel: { type: OptionType.STRING, default: "Directory", description: "Directory badge label", - onChange: reloadBadges, + restartNeeded: true, }, forumBadgeLabel: { type: OptionType.STRING, default: "Forum", description: "Forum badge label", - onChange: reloadBadges, + restartNeeded: true, }, mediaBadgeLabel: { type: OptionType.STRING, default: "Media", description: "Media badge label", - onChange: reloadBadges, + restartNeeded: true, }, nsfwBadgeLabel: { type: OptionType.STRING, default: "NSFW", description: "NSFW badge label", - onChange: reloadBadges, + restartNeeded: true, }, lockedBadgeLabel: { type: OptionType.STRING, default: "Locked", description: "Locked badge label", - onChange: reloadBadges, + restartNeeded: true, }, rulesBadgeLabel: { type: OptionType.STRING, default: "Rules", description: "Rules badge label", - onChange: reloadBadges, + restartNeeded: true, }, unknownBadgeLabel: { type: OptionType.STRING, default: "Unknown", description: "Unknown badge label", - onChange: reloadBadges, + restartNeeded: true, }, - textBadgeColor: { type: OptionType.STRING, description: "Text badge color", - onChange: reloadBadges, + restartNeeded: true, }, voiceBadgeColor: { type: OptionType.STRING, description: "Voice badge color", - onChange: reloadBadges, + restartNeeded: true, }, categoryBadgeColor: { type: OptionType.STRING, description: "Category badge color", - onChange: reloadBadges, + restartNeeded: true, }, announcementBadgeColor: { type: OptionType.STRING, description: "Announcement badge color", - onChange: reloadBadges, + restartNeeded: true, }, announcementThreadBadgeColor: { type: OptionType.STRING, description: "Announcement Thread badge color", - onChange: reloadBadges, + restartNeeded: true, }, publicThreadBadgeColor: { type: OptionType.STRING, description: "Public Thread badge color", - onChange: reloadBadges, + restartNeeded: true, }, privateThreadBadgeColor: { type: OptionType.STRING, description: "Private Thread badge color", - onChange: reloadBadges, + restartNeeded: true, }, stageBadgeColor: { type: OptionType.STRING, description: "Stage badge color", - onChange: reloadBadges, + restartNeeded: true, }, directoryBadgeColor: { type: OptionType.STRING, description: "Directory badge color", - onChange: reloadBadges, + restartNeeded: true, }, forumBadgeColor: { type: OptionType.STRING, description: "Forum badge color", - onChange: reloadBadges, + restartNeeded: true, }, mediaBadgeColor: { type: OptionType.STRING, description: "Media badge color", - onChange: reloadBadges, + restartNeeded: true, }, nsfwBadgeColor: { type: OptionType.STRING, description: "NSFW badge color", - onChange: reloadBadges, + restartNeeded: true, }, lockedBadgeColor: { type: OptionType.STRING, description: "Locked badge color", - onChange: reloadBadges, + restartNeeded: true, }, rulesBadgeColor: { type: OptionType.STRING, description: "Rules badge color", - onChange: reloadBadges, + restartNeeded: true, }, unknownBadgeColor: { type: OptionType.STRING, description: "Unknown badge color", - onChange: reloadBadges, + restartNeeded: true, }, }); -const defaultValues = { +export const defaultValues = { showTextBadge: true, showVoiceBadge: true, showCategoryBadge: true, @@ -314,7 +311,7 @@ const defaultValues = { nsfwBadgeTooltip: "This channel is marked as NSFW.", }; -function isEnabled(type: number) { +export function isEnabled(type: number) { const fromValues = settings.store; switch (type) { @@ -351,7 +348,7 @@ function isEnabled(type: number) { } } -function returnChannelBadge(type: number) { +export function returnChannelBadge(type: number) { switch (type) { case 0: return { css: "text", label: settings.store.textBadgeLabel, color: settings.store.textBadgeColor }; @@ -385,5 +382,3 @@ function returnChannelBadge(type: number) { return { css: "unknown", label: settings.store.unknownBadgeLabel, color: settings.store.unknownBadgeColor }; } } - -export { defaultValues, isEnabled, returnChannelBadge, settings }; diff --git a/src/equicordplugins/fastDeleteChannels/index.tsx b/src/equicordplugins/fastDeleteChannels/index.tsx index 2a026500a..b112ca46a 100644 --- a/src/equicordplugins/fastDeleteChannels/index.tsx +++ b/src/equicordplugins/fastDeleteChannels/index.tsx @@ -78,6 +78,7 @@ export default definePlugin({ settings, patches: [ // TY TypingIndicator + // Normal Channels { find: "UNREAD_IMPORTANT:", replacement: { @@ -85,6 +86,7 @@ export default definePlugin({ replace: "$&,$self.TrashIcon($1)" } }, + // Threads { find: "spineWithGuildIcon]:", replacement: {