From 827a2dfa41038350d6068a3e8258533f5745db71 Mon Sep 17 00:00:00 2001 From: Creation's Date: Thu, 10 Oct 2024 15:17:57 -0400 Subject: [PATCH] feat(plugin): CharacterCounter (#48) * add plugin CharacterCounter * update readme * Never use the ' please * Update index.tsx fix charmax? * Fixes --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- README.md | 5 +- .../CharacterCounter/index.tsx | 138 ++++++++++++++++++ .../CharacterCounter/style.css | 26 ++++ .../mediaDownloader.desktop/index.tsx | 6 +- 4 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 src/equicordplugins/CharacterCounter/index.tsx create mode 100644 src/equicordplugins/CharacterCounter/style.css diff --git a/README.md b/README.md index 5577fad1..159e1ce0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-126 additional plugins +127 additional plugins - AllCallTimers by MaxHerbold & D3SOX - AltKrispSwitch by newwares @@ -34,6 +34,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - CuteAnimeBoys by ShadyGoat - CuteNekos by echo - CutePats by thororen +- CharacterCounter by Creations - Demonstration by Samwich - DisableCameras by Joona - DoNotLeak by Perny @@ -67,7 +68,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - InRole by nin0dev - IrcColors by Grzesiek11 - IRememberYou by zoodogood -- ImagePreview by Creation's +- ImagePreview by Creations - Jumpscare by Surgedevs - JumpToStart by Samwich - KeyboardSounds by HypedDomi diff --git a/src/equicordplugins/CharacterCounter/index.tsx b/src/equicordplugins/CharacterCounter/index.tsx new file mode 100644 index 00000000..48f0b8cc --- /dev/null +++ b/src/equicordplugins/CharacterCounter/index.tsx @@ -0,0 +1,138 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { waitFor } from "@webpack"; +import { UserStore } from "@webpack/common"; + +let ChannelTextAreaClasses; +let shouldShowColorEffects: boolean; +let position: boolean; + +waitFor(["buttonContainer", "channelTextArea"], m => (ChannelTextAreaClasses = m)); + +const settings = definePluginSettings({ + colorEffects: { + type: OptionType.BOOLEAN, + description: "Turn on or off color effects for getting close to the character limit", + default: true, + onChange: value => { + shouldShowColorEffects = value; + } + }, + position: { + type: OptionType.BOOLEAN, + description: "Move the character counter to the left side of the chat input", + default: false, + onChange: value => { + position = value; + + const charCounterDiv = document.querySelector(".char-counter"); + if (charCounterDiv) { + if (value) { + charCounterDiv.classList.add("left"); + } else { + charCounterDiv.classList.remove("left"); + } + } + } + } +}); + +export default definePlugin({ + name: "CharacterCounter", + description: "Adds a character counter to the chat input", + authors: [EquicordDevs.creations], + settings: settings, + + start() { + const premiumType = (UserStore.getCurrentUser().premiumType ?? 0); + const charMax = premiumType === 2 ? 4000 : 2000; + + shouldShowColorEffects = settings.store.colorEffects; + position = settings.store.position; + + const addCharCounter = () => { + const chatTextArea: HTMLElement | null = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`); + if (!chatTextArea) return; + + let charCounterDiv: HTMLElement | null = document.querySelector(".char-counter"); + if (!charCounterDiv) { + charCounterDiv = document.createElement("div"); + charCounterDiv.classList.add("char-counter"); + + if (position) charCounterDiv.classList.add("left"); + + charCounterDiv.innerHTML = `0/${charMax}`; + } + + const chatInputContainer: HTMLElement | null = chatTextArea.closest("form"); + if (chatInputContainer && !chatInputContainer.contains(charCounterDiv)) { + chatTextArea.style.display = "flex"; + chatTextArea.style.flexDirection = "column"; + chatCounterPositionUpdate(chatTextArea, charCounterDiv); + + chatTextArea.appendChild(charCounterDiv); + } + + const chatInput: HTMLElement | null = chatTextArea.querySelector('div[contenteditable="true"]'); + + const updateCharCount = () => { + const text = chatInput?.textContent?.replace(/[\uFEFF\xA0]/g, "") || ""; + const charCount = text.trim().length; + const charCountSpan: HTMLElement | null = charCounterDiv!.querySelector(".char-count"); + charCountSpan!.textContent = `${charCount}`; + + if (shouldShowColorEffects) { + const percentage = (charCount / charMax) * 100; + let color; + if (percentage < 50) { + color = "#888"; + } else if (percentage < 75) { + color = "#ff9900"; + } else if (percentage < 90) { + color = "#ff6600"; + } else { + color = "#ff0000"; + } + charCountSpan!.style.color = color; + } + }; + + chatInput?.addEventListener("input", updateCharCount); + chatInput?.addEventListener("keydown", () => setTimeout(updateCharCount, 0)); + chatInput?.addEventListener("paste", () => setTimeout(updateCharCount, 0)); + }; + + const chatCounterPositionUpdate = (chatTextArea: HTMLElement, charCounterDiv: HTMLElement) => { + const position = "flex-end"; + chatTextArea.style.justifyContent = position; + charCounterDiv.style.position = "absolute"; + }; + + const observeDOMChanges = () => { + const observer = new MutationObserver(() => { + const chatTextArea = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`); + if (chatTextArea) { + addCharCounter(); + } + }); + + observer.observe(document.body, { childList: true, subtree: true }); + }; + + observeDOMChanges(); + }, + + stop() { + const charCounterDiv = document.querySelector(".char-counter"); + if (charCounterDiv) charCounterDiv.remove(); + } +}); diff --git a/src/equicordplugins/CharacterCounter/style.css b/src/equicordplugins/CharacterCounter/style.css new file mode 100644 index 00000000..3b160741 --- /dev/null +++ b/src/equicordplugins/CharacterCounter/style.css @@ -0,0 +1,26 @@ +.char-counter { + font-size: 12px; + color: var(--text-muted); + text-align: right; + position: absolute; + bottom: -15px; + right: 0; + pointer-events: none; + z-index: 1; + margin: 0; + padding: 0; +} + +.char-counter.left { + text-align: left; + right: auto; + left: 0; +} + +.char-counter .char-count { + transition: color 0.3s ease; +} + +.char-counter .char-max { + color: var(--text-muted); +} \ No newline at end of file diff --git a/src/equicordplugins/mediaDownloader.desktop/index.tsx b/src/equicordplugins/mediaDownloader.desktop/index.tsx index ebc27861..f7bce69f 100644 --- a/src/equicordplugins/mediaDownloader.desktop/index.tsx +++ b/src/equicordplugins/mediaDownloader.desktop/index.tsx @@ -41,9 +41,9 @@ const logger = new Logger("MediaDownloader", "#ff0b01"); const maxFileSize = () => { const premiumType = (UserStore.getCurrentUser().premiumType ?? 0); - if (premiumType > 1) return 500000000; // 500MB - if (premiumType > 0) return 50000000; // 50MB - return 25000000; // 25MB + if (premiumType === 2) return 500000000; // Nitro 500MB + if (premiumType === 1 || premiumType === 3) return 50000000; // Classic || Basic 50MB + return 25000000; // Base 25MB }; /** Takes a string and splits it into an array of arguments. */ const argParse = (args: string): string[] => args.match(