Redo ChannelBadges
This commit is contained in:
parent
03e9c2b501
commit
708bce9a91
4 changed files with 143 additions and 195 deletions
|
|
@ -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<Element>();
|
||||
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);
|
||||
}
|
||||
});
|
||||
91
src/equicordplugins/channelBadges/index.tsx
Normal file
91
src/equicordplugins/channelBadges/index.tsx
Normal file
|
|
@ -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 (
|
||||
<div
|
||||
key={id}
|
||||
className={`channel-badge channel-badge-${css}`}
|
||||
style={color ? { backgroundColor: color } : undefined}
|
||||
title={title}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 <div className="badge-container">{badges}</div>;
|
||||
}
|
||||
});
|
||||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue