mirror of
https://github.com/Equicord/Equicord.git
synced 2025-01-18 13:23:28 -05:00
feat(plugin): ChannelBadges (#51)
* start of channelbadges * update badges * give in and add colors to settings page, refactor * change from styles.css to style.css * update for linter * forgot to remove dm css no need for them * Fixes --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
b80d12a5cc
commit
9dd8a65eb2
3 changed files with 598 additions and 0 deletions
140
src/equicordplugins/channelBadges/index.ts
Normal file
140
src/equicordplugins/channelBadges/index.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 { ChannelStore, SelectedGuildStore } from "@webpack/common";
|
||||
import { Channel, Guild } from "discord-types/general";
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
389
src/equicordplugins/channelBadges/settings.tsx
Normal file
389
src/equicordplugins/channelBadges/settings.tsx
Normal file
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { OptionType } from "@utils/types";
|
||||
|
||||
import { reloadBadges } from "./index";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
oneBadgePerChannel: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
description: "Show only one badge per channel",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showTextBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Text badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showVoiceBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Voice badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showCategoryBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Category badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showDirectoryBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Directory badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showAnnouncementThreadBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Announcement Thread badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showPublicThreadBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Public Thread badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showPrivateThreadBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Private Thread badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showStageBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Stage badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showAnnouncementBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Announcement badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showForumBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Forum badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showMediaBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Media badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showNSFWBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show NSFW badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showLockedBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Locked badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showRulesBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Rules badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
showUnknownBadge: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show Unknown badge",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
|
||||
textBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Text",
|
||||
description: "Text badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
voiceBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Voice",
|
||||
description: "Voice badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
categoryBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Category",
|
||||
description: "Category badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
announcementBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "News",
|
||||
description: "Announcement badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
announcementThreadBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "News Thread",
|
||||
description: "Announcement Thread badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
publicThreadBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Thread",
|
||||
description: "Public Thread badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
privateThreadBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Private Thread",
|
||||
description: "Private Thread badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
stageBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Stage",
|
||||
description: "Stage badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
directoryBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Directory",
|
||||
description: "Directory badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
forumBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Forum",
|
||||
description: "Forum badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
mediaBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Media",
|
||||
description: "Media badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
nsfwBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "NSFW",
|
||||
description: "NSFW badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
lockedBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Locked",
|
||||
description: "Locked badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
rulesBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Rules",
|
||||
description: "Rules badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
unknownBadgeLabel: {
|
||||
type: OptionType.STRING,
|
||||
default: "Unknown",
|
||||
description: "Unknown badge label",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
|
||||
|
||||
textBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Text badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
voiceBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Voice badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
categoryBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Category badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
announcementBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Announcement badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
announcementThreadBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Announcement Thread badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
publicThreadBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Public Thread badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
privateThreadBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Private Thread badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
stageBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Stage badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
directoryBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Directory badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
forumBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Forum badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
mediaBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Media badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
nsfwBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "NSFW badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
lockedBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Locked badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
rulesBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Rules badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
unknownBadgeColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Unknown badge color",
|
||||
onChange: reloadBadges,
|
||||
},
|
||||
});
|
||||
|
||||
const defaultValues = {
|
||||
showTextBadge: true,
|
||||
showVoiceBadge: true,
|
||||
showCategoryBadge: true,
|
||||
showAnnouncementBadge: true,
|
||||
showAnnouncementThreadBadge: true,
|
||||
showPublicThreadBadge: true,
|
||||
showPrivateThreadBadge: true,
|
||||
showStageBadge: true,
|
||||
showDirectoryBadge: true,
|
||||
showForumBadge: true,
|
||||
showMediaBadge: true,
|
||||
showNSFWBadge: true,
|
||||
showLockedBadge: true,
|
||||
showRulesBadge: true,
|
||||
showUnknownBadge: true,
|
||||
|
||||
channelBadges: {
|
||||
text: "Text",
|
||||
voice: "Voice",
|
||||
category: "Category",
|
||||
announcement: "News",
|
||||
announcement_thread: "News Thread",
|
||||
public_thread: "Thread",
|
||||
private_thread: "Private Thread",
|
||||
stage: "Stage",
|
||||
directory: "Directory",
|
||||
forum: "Forum",
|
||||
media: "Media",
|
||||
nsfw: "NSFW",
|
||||
locked: "Locked",
|
||||
rules: "Rules",
|
||||
unknown: "Unknown"
|
||||
},
|
||||
lockedBadgeTooltip: "This channel is locked.",
|
||||
nsfwBadgeTooltip: "This channel is marked as NSFW.",
|
||||
};
|
||||
|
||||
function isEnabled(type: number) {
|
||||
const fromValues = settings.store;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
return fromValues.showTextBadge;
|
||||
case 2:
|
||||
return fromValues.showVoiceBadge;
|
||||
case 4:
|
||||
return fromValues.showCategoryBadge;
|
||||
case 5:
|
||||
return fromValues.showAnnouncementBadge;
|
||||
case 10:
|
||||
return fromValues.showAnnouncementThreadBadge;
|
||||
case 11:
|
||||
return fromValues.showPublicThreadBadge;
|
||||
case 12:
|
||||
return fromValues.showPrivateThreadBadge;
|
||||
case 13:
|
||||
return fromValues.showStageBadge;
|
||||
case 14:
|
||||
return fromValues.showDirectoryBadge;
|
||||
case 15:
|
||||
return fromValues.showForumBadge;
|
||||
case 16:
|
||||
return fromValues.showMediaBadge;
|
||||
case 6100:
|
||||
return fromValues.showNSFWBadge;
|
||||
case 6101:
|
||||
return fromValues.showLockedBadge;
|
||||
case 6102:
|
||||
return fromValues.showRulesBadge;
|
||||
default:
|
||||
return fromValues.showUnknownBadge;
|
||||
}
|
||||
}
|
||||
|
||||
function returnChannelBadge(type: number) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
return { css: "text", label: settings.store.textBadgeLabel, color: settings.store.textBadgeColor };
|
||||
case 2:
|
||||
return { css: "voice", label: settings.store.voiceBadgeLabel, color: settings.store.voiceBadgeColor };
|
||||
case 4:
|
||||
return { css: "category", label: settings.store.categoryBadgeLabel, color: settings.store.categoryBadgeColor };
|
||||
case 5:
|
||||
return { css: "announcement", label: settings.store.announcementBadgeLabel, color: settings.store.announcementBadgeColor };
|
||||
case 10:
|
||||
return { css: "announcement-thread", label: settings.store.announcementThreadBadgeLabel, color: settings.store.announcementThreadBadgeColor };
|
||||
case 11:
|
||||
return { css: "thread", label: settings.store.publicThreadBadgeLabel, color: settings.store.publicThreadBadgeColor };
|
||||
case 12:
|
||||
return { css: "private-thread", label: settings.store.privateThreadBadgeLabel, color: settings.store.privateThreadBadgeColor };
|
||||
case 13:
|
||||
return { css: "stage", label: settings.store.stageBadgeLabel, color: settings.store.stageBadgeColor };
|
||||
case 14:
|
||||
return { css: "directory", label: settings.store.directoryBadgeLabel, color: settings.store.directoryBadgeColor };
|
||||
case 15:
|
||||
return { css: "forum", label: settings.store.forumBadgeLabel, color: settings.store.forumBadgeColor };
|
||||
case 16:
|
||||
return { css: "media", label: settings.store.mediaBadgeLabel, color: settings.store.mediaBadgeColor };
|
||||
case 6100:
|
||||
return { css: "nsfw", label: settings.store.nsfwBadgeLabel, color: settings.store.nsfwBadgeColor };
|
||||
case 6101:
|
||||
return { css: "locked", label: settings.store.lockedBadgeLabel, color: settings.store.lockedBadgeColor };
|
||||
case 6102:
|
||||
return { css: "rules", label: settings.store.rulesBadgeLabel, color: settings.store.rulesBadgeColor };
|
||||
default:
|
||||
return { css: "unknown", label: settings.store.unknownBadgeLabel, color: settings.store.unknownBadgeColor };
|
||||
}
|
||||
}
|
||||
|
||||
export { defaultValues, isEnabled, returnChannelBadge, settings };
|
69
src/equicordplugins/channelBadges/style.css
Normal file
69
src/equicordplugins/channelBadges/style.css
Normal file
|
@ -0,0 +1,69 @@
|
|||
.channel-badge {
|
||||
margin-left: 0.2rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
color: var(--white);
|
||||
order: 2;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.badge-container {
|
||||
display: flex;
|
||||
gap: 0.1rem;
|
||||
align-items: center;
|
||||
margin-left: 0.2rem;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.channel-badge-text {
|
||||
background-color: #5865F2;
|
||||
}
|
||||
|
||||
.channel-badge-voice {
|
||||
background-color: #3BA55D;
|
||||
}
|
||||
|
||||
.channel-badge-category {
|
||||
background-color: #4F545C;
|
||||
}
|
||||
|
||||
.channel-badge-directory {
|
||||
background-color: #FAA61A;
|
||||
}
|
||||
|
||||
.channel-badge-thread,
|
||||
.channel-badge-public-thread,
|
||||
.channel-badge-announcement-thread,
|
||||
.channel-badge-private-thread {
|
||||
background-color: #7289DA;
|
||||
}
|
||||
|
||||
.channel-badge-announcement,
|
||||
.channel-badge-announcement-channel {
|
||||
background-color: #FAA61A;
|
||||
}
|
||||
|
||||
.channel-badge-rules,
|
||||
.channel-badge-stage,
|
||||
.channel-badge-media,
|
||||
.channel-badge-forum {
|
||||
background-color: #5865F2;
|
||||
}
|
||||
|
||||
.channel-badge-nsfw {
|
||||
background-color: #E91E63;
|
||||
}
|
||||
|
||||
.channel-badge-locked {
|
||||
background-color: #B9BBBE;
|
||||
}
|
||||
|
||||
.channel-badge-unknown {
|
||||
background-color: #747F8D;
|
||||
}
|
Loading…
Reference in a new issue