mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-20 03:47:01 -04:00
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>
This commit is contained in:
parent
28cc15ae5b
commit
827a2dfa41
4 changed files with 170 additions and 5 deletions
|
@ -10,7 +10,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
||||||
|
|
||||||
### Extra included plugins
|
### Extra included plugins
|
||||||
<details>
|
<details>
|
||||||
<summary>126 additional plugins</summary>
|
<summary>127 additional plugins</summary>
|
||||||
|
|
||||||
- AllCallTimers by MaxHerbold & D3SOX
|
- AllCallTimers by MaxHerbold & D3SOX
|
||||||
- AltKrispSwitch by newwares
|
- AltKrispSwitch by newwares
|
||||||
|
@ -34,6 +34,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
||||||
- CuteAnimeBoys by ShadyGoat
|
- CuteAnimeBoys by ShadyGoat
|
||||||
- CuteNekos by echo
|
- CuteNekos by echo
|
||||||
- CutePats by thororen
|
- CutePats by thororen
|
||||||
|
- CharacterCounter by Creations
|
||||||
- Demonstration by Samwich
|
- Demonstration by Samwich
|
||||||
- DisableCameras by Joona
|
- DisableCameras by Joona
|
||||||
- DoNotLeak by Perny
|
- DoNotLeak by Perny
|
||||||
|
@ -67,7 +68,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
||||||
- InRole by nin0dev
|
- InRole by nin0dev
|
||||||
- IrcColors by Grzesiek11
|
- IrcColors by Grzesiek11
|
||||||
- IRememberYou by zoodogood
|
- IRememberYou by zoodogood
|
||||||
- ImagePreview by Creation's
|
- ImagePreview by Creations
|
||||||
- Jumpscare by Surgedevs
|
- Jumpscare by Surgedevs
|
||||||
- JumpToStart by Samwich
|
- JumpToStart by Samwich
|
||||||
- KeyboardSounds by HypedDomi
|
- KeyboardSounds by HypedDomi
|
||||||
|
|
138
src/equicordplugins/CharacterCounter/index.tsx
Normal file
138
src/equicordplugins/CharacterCounter/index.tsx
Normal file
|
@ -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 = `<span class="char-count">0</span>/<span class="char-max">${charMax}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
26
src/equicordplugins/CharacterCounter/style.css
Normal file
26
src/equicordplugins/CharacterCounter/style.css
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -41,9 +41,9 @@ const logger = new Logger("MediaDownloader", "#ff0b01");
|
||||||
|
|
||||||
const maxFileSize = () => {
|
const maxFileSize = () => {
|
||||||
const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
|
const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
|
||||||
if (premiumType > 1) return 500000000; // 500MB
|
if (premiumType === 2) return 500000000; // Nitro 500MB
|
||||||
if (premiumType > 0) return 50000000; // 50MB
|
if (premiumType === 1 || premiumType === 3) return 50000000; // Classic || Basic 50MB
|
||||||
return 25000000; // 25MB
|
return 25000000; // Base 25MB
|
||||||
};
|
};
|
||||||
/** Takes a string and splits it into an array of arguments. */
|
/** Takes a string and splits it into an array of arguments. */
|
||||||
const argParse = (args: string): string[] => args.match(
|
const argParse = (args: string): string[] => args.match(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue