mirror of
https://github.com/Equicord/Equicord.git
synced 2025-01-19 05:43:35 -05:00
feat(charCounter): fix position and qols (#50)
* squash (#10) :3 * whar * inline this * fixes by creations
This commit is contained in:
parent
0e0674fd98
commit
22d772c8d6
2 changed files with 107 additions and 98 deletions
|
@ -8,17 +8,13 @@ import "./style.css";
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { EquicordDevs } from "@utils/constants";
|
import { EquicordDevs } from "@utils/constants";
|
||||||
import { getCurrentChannel } from "@utils/discord";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { waitFor } from "@webpack";
|
import { waitFor } from "@webpack";
|
||||||
import { UserStore } from "@webpack/common";
|
import { SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
let ChannelTextAreaClasses;
|
let ChannelTextAreaClasses: Record<string, string> | null = null;
|
||||||
let shouldShowColorEffects: boolean;
|
let shouldShowColorEffects: boolean;
|
||||||
let position: boolean;
|
let position: boolean;
|
||||||
let forceLeft = false;
|
|
||||||
|
|
||||||
waitFor(["buttonContainer", "channelTextArea"], m => (ChannelTextAreaClasses = m));
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
colorEffects: {
|
colorEffects: {
|
||||||
|
@ -36,108 +32,119 @@ const settings = definePluginSettings({
|
||||||
onChange: value => {
|
onChange: value => {
|
||||||
position = value;
|
position = value;
|
||||||
|
|
||||||
const charCounterDiv = document.querySelector(".char-counter");
|
const charCounterDiv = document.querySelector(".vc-char-counter");
|
||||||
if (charCounterDiv) {
|
if (charCounterDiv) {
|
||||||
if (value) {
|
if (value) charCounterDiv.classList.add("left");
|
||||||
charCounterDiv.classList.add("left");
|
else charCounterDiv.classList.remove("left");
|
||||||
} else {
|
|
||||||
charCounterDiv.classList.remove("left");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
const updateCharCounterOnChannelChange = async () => {
|
||||||
name: "CharacterCounter",
|
await waitForChatInput();
|
||||||
description: "Adds a character counter to the chat input",
|
addCharCounter();
|
||||||
authors: [EquicordDevs.creations],
|
};
|
||||||
settings: settings,
|
|
||||||
|
const waitForChatInput = async () => {
|
||||||
|
return waitFor([`${ChannelTextAreaClasses?.channelTextArea}`], () => document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
const addCharCounter = () => {
|
||||||
|
const chatTextArea: HTMLElement | null = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`);
|
||||||
|
if (!chatTextArea) return;
|
||||||
|
|
||||||
|
let charCounterDiv: HTMLElement | null = document.querySelector(".vc-char-counter");
|
||||||
|
if (!charCounterDiv) {
|
||||||
|
charCounterDiv = document.createElement("div");
|
||||||
|
charCounterDiv.classList.add("vc-char-counter");
|
||||||
|
|
||||||
|
if (position) charCounterDiv.classList.add("left");
|
||||||
|
|
||||||
start() {
|
|
||||||
const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
|
const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
|
||||||
const charMax = premiumType === 2 ? 4000 : 2000;
|
const charMax = premiumType === 2 ? 4000 : 2000;
|
||||||
|
|
||||||
|
charCounterDiv.innerHTML = `<span class="vc-char-count">0</span>/<span class="vc-char-max">${charMax}</span>`;
|
||||||
|
charCounterDiv.style.opacity = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatInputContainer: HTMLElement | null = chatTextArea.closest("form");
|
||||||
|
if (chatInputContainer && !chatInputContainer.contains(charCounterDiv)) {
|
||||||
|
chatTextArea.style.display = "flex";
|
||||||
|
chatTextArea.style.flexDirection = "column";
|
||||||
|
chatCounterPositionUpdate(chatInputContainer, 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;
|
||||||
|
|
||||||
|
if (charCount !== 0) charCounterDiv.style.opacity = "1";
|
||||||
|
else charCounterDiv.style.opacity = "0";
|
||||||
|
|
||||||
|
const charCountSpan: HTMLElement | null = charCounterDiv!.querySelector(".vc-char-count");
|
||||||
|
charCountSpan!.textContent = `${charCount}`;
|
||||||
|
|
||||||
|
const bottomPos = chatInputContainer!.offsetHeight;
|
||||||
|
charCounterDiv.style.bottom = `${bottomPos.toString()}px`;
|
||||||
|
|
||||||
|
if (shouldShowColorEffects) {
|
||||||
|
const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
|
||||||
|
const charMax = premiumType === 2 ? 4000 : 2000;
|
||||||
|
const percentage = (charCount / charMax) * 100;
|
||||||
|
let color;
|
||||||
|
if (percentage < 50) {
|
||||||
|
color = "var(--text-muted)";
|
||||||
|
} else if (percentage < 75) {
|
||||||
|
color = "var(--yellow-330)";
|
||||||
|
} else if (percentage < 90) {
|
||||||
|
color = "var(--orange-330)";
|
||||||
|
} else {
|
||||||
|
color = "var(--red-360)";
|
||||||
|
}
|
||||||
|
charCountSpan!.style.color = color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
chatInput?.addEventListener("input", updateCharCount);
|
||||||
|
chatInput?.addEventListener("keydown", () => setTimeout(updateCharCount, 0));
|
||||||
|
chatInput?.addEventListener("paste", () => setTimeout(updateCharCount, 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
const chatCounterPositionUpdate = (chatInputContainer: HTMLElement, chatTextArea: HTMLElement, charCounterDiv: HTMLElement) => {
|
||||||
|
chatTextArea.style.justifyContent = "flex-end";
|
||||||
|
charCounterDiv.style.position = "absolute";
|
||||||
|
|
||||||
|
const bottomPos = (chatInputContainer.offsetHeight) - 10; // onload 68px, minus 10 to fix
|
||||||
|
charCounterDiv.style.bottom = `${bottomPos.toString()}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "CharacterCounter",
|
||||||
|
description: "Adds a character counter to the chat input",
|
||||||
|
authors: [EquicordDevs.creations, EquicordDevs.Panniku],
|
||||||
|
settings: settings,
|
||||||
|
|
||||||
|
start: async () => {
|
||||||
shouldShowColorEffects = settings.store.colorEffects;
|
shouldShowColorEffects = settings.store.colorEffects;
|
||||||
position = settings.store.position;
|
position = settings.store.position;
|
||||||
|
|
||||||
const addCharCounter = () => {
|
waitFor(["buttonContainer", "channelTextArea"], m => (ChannelTextAreaClasses = m));
|
||||||
const chatTextArea: HTMLElement | null = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`);
|
await updateCharCounterOnChannelChange();
|
||||||
if (!chatTextArea) return;
|
|
||||||
|
|
||||||
let charCounterDiv: HTMLElement | null = document.querySelector(".char-counter");
|
SelectedChannelStore.addChangeListener(updateCharCounterOnChannelChange);
|
||||||
if (!charCounterDiv) {
|
SelectedGuildStore.addChangeListener(updateCharCounterOnChannelChange);
|
||||||
charCounterDiv = document.createElement("div");
|
|
||||||
charCounterDiv.classList.add("char-counter");
|
|
||||||
|
|
||||||
if (position || forceLeft) 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 && !document.querySelector(".char-counter")) {
|
|
||||||
const currentChannel = getCurrentChannel();
|
|
||||||
forceLeft = currentChannel?.rateLimitPerUser !== 0;
|
|
||||||
|
|
||||||
addCharCounter();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
observeDOMChanges();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
const charCounterDiv = document.querySelector(".char-counter");
|
const charCounterDiv = document.querySelector(".vc-char-counter");
|
||||||
if (charCounterDiv) charCounterDiv.remove();
|
if (charCounterDiv) charCounterDiv.remove();
|
||||||
|
|
||||||
|
SelectedChannelStore.removeChangeListener(updateCharCounterOnChannelChange);
|
||||||
|
SelectedGuildStore.removeChangeListener(updateCharCounterOnChannelChange);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
.char-counter {
|
.vc-char-counter {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -15px;
|
|
||||||
right: 0;
|
right: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 6px;
|
||||||
|
background-color: var(--background-tertiary);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.char-counter.left {
|
.vc-char-counter.left {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.char-counter .char-count {
|
.vc-char-counter .vc-char-count {
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.char-counter .char-max {
|
.vc-char-counter .vc-char-max {
|
||||||
color: var(--text-muted);
|
color: var(--primary-330);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue