From 32817aa039f150b737b62cec54fe246c57256765 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 13:55:38 -0400 Subject: [PATCH 01/12] Fix broken patch --- src/plugins/_api/serverList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 193f0588..262def65 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -39,7 +39,7 @@ export default definePlugin({ replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" }, { - match: /guildDiscoveryRef.{0,300}\{\}\)\]\}\)\]/, + match: /guildDiscoveryRef.{0,300}\}\)\]\}\)\}\)/, replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" } ] From cb4b6b75544672278b8341364928e4ee0877109a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:00:04 -0400 Subject: [PATCH 02/12] Remove HideServers Temp --- .../hideServers/HiddenServersStore.ts | 58 ------- .../components/HiddenServersButton.tsx | 37 ----- .../components/HiddenServersMenu.tsx | 105 ------------- .../hideServers/components/style.css | 47 ------ src/equicordplugins/hideServers/index.tsx | 141 ------------------ src/equicordplugins/hideServers/settings.tsx | 51 ------- src/plugins/_api/serverList.ts | 14 +- 7 files changed, 4 insertions(+), 449 deletions(-) delete mode 100644 src/equicordplugins/hideServers/HiddenServersStore.ts delete mode 100644 src/equicordplugins/hideServers/components/HiddenServersButton.tsx delete mode 100644 src/equicordplugins/hideServers/components/HiddenServersMenu.tsx delete mode 100644 src/equicordplugins/hideServers/components/style.css delete mode 100644 src/equicordplugins/hideServers/index.tsx delete mode 100644 src/equicordplugins/hideServers/settings.tsx diff --git a/src/equicordplugins/hideServers/HiddenServersStore.ts b/src/equicordplugins/hideServers/HiddenServersStore.ts deleted file mode 100644 index bdad64fd..00000000 --- a/src/equicordplugins/hideServers/HiddenServersStore.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import * as DataStore from "@api/DataStore"; -import { findStoreLazy, proxyLazyWebpack } from "@webpack"; -import { Flux, FluxDispatcher, GuildStore } from "@webpack/common"; -import { Guild } from "discord-types/general"; - - -export const HiddenServersStore = proxyLazyWebpack(() => { - const { Store } = Flux; - - const SortedGuildStore = findStoreLazy("SortedGuildStore"); - const DB_KEY = "HideServers_servers"; - - class HiddenServersStore extends Store { - private _hiddenGuilds: Set = new Set(); - public get hiddenGuilds() { - return this._hiddenGuilds; - } - // id try to use .initialize() but i dont know how it works - public async load() { - const data = await DataStore.get(DB_KEY); - if (data) { - this._hiddenGuilds = data; - } - } - public unload() { - this._hiddenGuilds.clear(); - } - - public addHidden(guild: Guild) { - this._hiddenGuilds.add(guild.id); - DataStore.set(DB_KEY, this._hiddenGuilds); - this.emitChange(); - } - public removeHidden(id: string) { - this._hiddenGuilds.delete(id); - DataStore.set(DB_KEY, this._hiddenGuilds); - this.emitChange(); - } - public clearHidden() { - this._hiddenGuilds.clear(); - DataStore.del(DB_KEY); - this.emitChange(); - } - public hiddenGuildsDetail(): Guild[] { - const sortedGuildIds = SortedGuildStore.getFlattenedGuildIds() as string[]; - // otherwise the list is in order of increasing id number which is confusing - return sortedGuildIds.filter(id => this._hiddenGuilds.has(id)).map(id => GuildStore.getGuild(id)); - } - } - - return new HiddenServersStore(FluxDispatcher); -}); diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx deleted file mode 100644 index 9cad8482..00000000 --- a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx +++ /dev/null @@ -1,37 +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 { classNameFactory } from "@api/Styles"; -import { Button, ButtonLooks, GuildStore, useStateFromStores } from "@webpack/common"; - -import { HiddenServersStore } from "../HiddenServersStore"; -import { openHiddenServersModal } from "./HiddenServersMenu"; - -const cl = classNameFactory("vc-hideservers-"); - -function HiddenServersButton() { - const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); - // if youve left a server dont show it in the count - const actuallyHidden = Array.from(hiddenGuilds).filter(x => GuildStore.getGuild(x)).length; - return ( -
- {actuallyHidden > 0 ? ( - - ) : null} -
- ); -} - -export default () => { return ; }; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx deleted file mode 100644 index 2673e1d4..00000000 --- a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { classNameFactory } from "@api/Styles"; -import { classes } from "@utils/misc"; -import { - closeModal, - ModalCloseButton, - ModalContent, - ModalHeader, - ModalProps, - ModalRoot, - ModalSize, - openModal, -} from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { Button, Forms, IconUtils, Text, useStateFromStores } from "@webpack/common"; -import { Guild } from "discord-types/general"; - -import { HiddenServersStore } from "../HiddenServersStore"; - -const cl = classNameFactory("vc-hideservers-"); -const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); - -function HiddenServersModal({ - modalProps, - close, -}: { - modalProps: ModalProps; - close(): void; -}) { - const servers = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); - return ( - - - - Hidden Servers - - - - - - - - - ); -} - -export function HiddenServersMenu({ servers }: { servers: Guild[]; }) { - return
- {servers.length > 0 ? ( - servers.map(server => ( -
-
- {server.icon - ? - :
- {server.acronym} -
- } -
- - {server.name} - - -
- )) - ) : ( - - No hidden servers - - )} -
; -} - -export function openHiddenServersModal() { - const key = openModal(modalProps => { - return ( - closeModal(key)} - /> - ); - }); -} diff --git a/src/equicordplugins/hideServers/components/style.css b/src/equicordplugins/hideServers/components/style.css deleted file mode 100644 index ee553841..00000000 --- a/src/equicordplugins/hideServers/components/style.css +++ /dev/null @@ -1,47 +0,0 @@ -.vc-hideservers-button-wrapper { - display: flex; - justify-content: center; - margin: 0 0 8px; -} - -.vc-hideservers-button { - max-width: 48px; - font-size: 60%; - background-color: var(--background-primary); - color: var(--header-secondary); -} - -.vc-hideservers-list { - flex: 1 1 auto; -} - -/* copied from blocked row */ -.vc-hideservers-row { - height: 62px; - display: flex; - align-items: center; - flex-direction: row; - margin-left: 20px; - margin-right: 20px; - border-top: 1px solid var(--background-modifier-accent); - border-bottom: 1px solid transparent; - justify-content: space-between; -} - -.vc-hideservers-guildicon { - display: flex; - align-items: center; - margin-right: 1em; -} - -.vc-hideservers-guildicon img { - border-radius: 50%; -} - -.vc-hideservers-name { - flex-grow: 1; -} - -.vc-hideservers-row-button { - margin-left: auto; -} diff --git a/src/equicordplugins/hideServers/index.tsx b/src/equicordplugins/hideServers/index.tsx deleted file mode 100644 index a7dfbd4a..00000000 --- a/src/equicordplugins/hideServers/index.tsx +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -// additional thanks to mwittrien/DevilBro and nexpid for their server hiding plugins, which served as inspiration - -import { - findGroupChildrenByChildId, - NavContextMenuPatchCallback, -} from "@api/ContextMenu"; -import { - addServerListElement, - removeServerListElement, - ServerListRenderPosition, -} from "@api/ServerList"; -import { EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; -import { Menu, React, useStateFromStores } from "@webpack/common"; -import { Guild } from "discord-types/general"; - -import hiddenServersButton from "./components/HiddenServersButton"; -import { HiddenServersStore } from "./HiddenServersStore"; -import settings from "./settings"; - -type guildsNode = { - type: "guild" | "folder"; - id: number | string; - children: guildsNode[]; -}; - -type qsResult = { - type: "GUILD" | string; - record?: { - id?: string; - guild_id?: string; - }; -}; - -const Patch: NavContextMenuPatchCallback = ( - children, - { guild }: { guild: Guild; } -) => { - const group = findGroupChildrenByChildId("privacy", children); - - group?.push( - HiddenServersStore.addHidden(guild)} - /> - ); -}; - -export function addIndicator() { - addServerListElement(ServerListRenderPosition.Below, hiddenServersButton); -} - -export function removeIndicator() { - removeServerListElement(ServerListRenderPosition.Below, hiddenServersButton); -} - -export default definePlugin({ - name: "HideServers", - description: "Allows you to hide servers from the guild list and quick switcher by right clicking them", - authors: [EquicordDevs.bep], - tags: ["guild", "server", "hide"], - - dependencies: ["ServerListAPI"], - contextMenus: { - "guild-context": Patch, - "guild-header-popout": Patch, - }, - patches: [ - { - find: '("guildsnav")', - replacement: [ - { - match: /(?<=Messages\.SERVERS,children:.{0,300}?)(\i)(\)?\.map\(\i\))/, - replace: "$self.useFilteredGuilds($1)$2", - }, - // despite my best efforts, the above doesnt trigger a rerender - { - match: /let{disableAppDownload.{0,10}isPlatformEmbedded/, - replace: "$self.useStore();$&", - } - ] - }, - { - find: "QUICKSWITCHER_PROTIP.format", - replacement: { - match: /(?<=renderResults\(\){)let{query/, - replace: "this.props.results = $self.filteredGuildResults(this.props.results);$&", - }, - }, - ], - settings, - useStore: () => { useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); }, - - async start() { - if (settings.store.showIndicator) { - addIndicator(); - } - await HiddenServersStore.load(); - }, - - async stop() { - removeIndicator(); - HiddenServersStore.unload(); - }, - - useFilteredGuilds(guilds: guildsNode[]): guildsNode[] { - const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); - return guilds.flatMap(guild => { - if (hiddenGuilds.has(guild.id.toString())) { - return []; - } - const newGuild = Object.assign({}, guild); - newGuild.children = guild.children.filter( - child => !hiddenGuilds.has(child.id.toString()) - ); - - return [newGuild]; - }); - }, - - filteredGuildResults(results: qsResult[]): qsResult[] { - // not used in a component so no useStateFromStore - const { hiddenGuilds } = HiddenServersStore; - return results.filter(result => { - if (result?.record?.guild_id && hiddenGuilds.has(result.record.guild_id)) { - return false; - } - if (result.type === "GUILD" && hiddenGuilds.has(result.record!.id!)) { - return false; - } - return true; - }); - }, -}); diff --git a/src/equicordplugins/hideServers/settings.tsx b/src/equicordplugins/hideServers/settings.tsx deleted file mode 100644 index 1f684824..00000000 --- a/src/equicordplugins/hideServers/settings.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 { Button, useStateFromStores } from "@webpack/common"; - -import { addIndicator, removeIndicator } from "."; -import { HiddenServersMenu } from "./components/HiddenServersMenu"; -import { HiddenServersStore } from "./HiddenServersStore"; - -export default definePluginSettings({ - showIndicator: { - type: OptionType.BOOLEAN, - description: "Show menu to unhide servers at the bottom of the list", - default: true, - onChange: val => { - if (val) { - addIndicator(); - } else { - removeIndicator(); - } - } - }, - guildsList: { - type: OptionType.COMPONENT, - description: "Remove hidden servers", - component: () => { - const detail = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); - return ; - } - }, - resetHidden: { - type: OptionType.COMPONENT, - description: "Remove all hidden guilds from the list", - component: () => ( -
- -
- ), - }, -}); diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 262def65..7904e78b 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -33,16 +33,10 @@ export default definePlugin({ }, { find: "Messages.SERVERS,children", - replacement: [ - { - match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/, - replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" - }, - { - match: /guildDiscoveryRef.{0,300}\}\)\]\}\)\}\)/, - replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" - } - ] + replacement: { + match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/, + replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" + } } ] }); From 7eb6ed000901dbc33f4746dfad0a0a7cb646e1aa Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:00:18 -0400 Subject: [PATCH 03/12] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ff6cf50a..9fe95c58 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Grammar by Samwich - GrammarFix by unstream - HideMessage by Hanzy -- HideServers by bepvte - HolyNotes by Wolfie - HomeTyping by Samwich - HopOn by ImLvna From 23717467f3037565464056fd1b9341a5e7f8005a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:25:20 -0400 Subject: [PATCH 04/12] Revert "Remove HideServers Temp" This reverts commit cb4b6b75544672278b8341364928e4ee0877109a. --- .../hideServers/HiddenServersStore.ts | 58 +++++++ .../components/HiddenServersButton.tsx | 37 +++++ .../components/HiddenServersMenu.tsx | 105 +++++++++++++ .../hideServers/components/style.css | 47 ++++++ src/equicordplugins/hideServers/index.tsx | 141 ++++++++++++++++++ src/equicordplugins/hideServers/settings.tsx | 51 +++++++ src/plugins/_api/serverList.ts | 14 +- 7 files changed, 449 insertions(+), 4 deletions(-) create mode 100644 src/equicordplugins/hideServers/HiddenServersStore.ts create mode 100644 src/equicordplugins/hideServers/components/HiddenServersButton.tsx create mode 100644 src/equicordplugins/hideServers/components/HiddenServersMenu.tsx create mode 100644 src/equicordplugins/hideServers/components/style.css create mode 100644 src/equicordplugins/hideServers/index.tsx create mode 100644 src/equicordplugins/hideServers/settings.tsx diff --git a/src/equicordplugins/hideServers/HiddenServersStore.ts b/src/equicordplugins/hideServers/HiddenServersStore.ts new file mode 100644 index 00000000..bdad64fd --- /dev/null +++ b/src/equicordplugins/hideServers/HiddenServersStore.ts @@ -0,0 +1,58 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import * as DataStore from "@api/DataStore"; +import { findStoreLazy, proxyLazyWebpack } from "@webpack"; +import { Flux, FluxDispatcher, GuildStore } from "@webpack/common"; +import { Guild } from "discord-types/general"; + + +export const HiddenServersStore = proxyLazyWebpack(() => { + const { Store } = Flux; + + const SortedGuildStore = findStoreLazy("SortedGuildStore"); + const DB_KEY = "HideServers_servers"; + + class HiddenServersStore extends Store { + private _hiddenGuilds: Set = new Set(); + public get hiddenGuilds() { + return this._hiddenGuilds; + } + // id try to use .initialize() but i dont know how it works + public async load() { + const data = await DataStore.get(DB_KEY); + if (data) { + this._hiddenGuilds = data; + } + } + public unload() { + this._hiddenGuilds.clear(); + } + + public addHidden(guild: Guild) { + this._hiddenGuilds.add(guild.id); + DataStore.set(DB_KEY, this._hiddenGuilds); + this.emitChange(); + } + public removeHidden(id: string) { + this._hiddenGuilds.delete(id); + DataStore.set(DB_KEY, this._hiddenGuilds); + this.emitChange(); + } + public clearHidden() { + this._hiddenGuilds.clear(); + DataStore.del(DB_KEY); + this.emitChange(); + } + public hiddenGuildsDetail(): Guild[] { + const sortedGuildIds = SortedGuildStore.getFlattenedGuildIds() as string[]; + // otherwise the list is in order of increasing id number which is confusing + return sortedGuildIds.filter(id => this._hiddenGuilds.has(id)).map(id => GuildStore.getGuild(id)); + } + } + + return new HiddenServersStore(FluxDispatcher); +}); diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx new file mode 100644 index 00000000..9cad8482 --- /dev/null +++ b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx @@ -0,0 +1,37 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import { classNameFactory } from "@api/Styles"; +import { Button, ButtonLooks, GuildStore, useStateFromStores } from "@webpack/common"; + +import { HiddenServersStore } from "../HiddenServersStore"; +import { openHiddenServersModal } from "./HiddenServersMenu"; + +const cl = classNameFactory("vc-hideservers-"); + +function HiddenServersButton() { + const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); + // if youve left a server dont show it in the count + const actuallyHidden = Array.from(hiddenGuilds).filter(x => GuildStore.getGuild(x)).length; + return ( +
+ {actuallyHidden > 0 ? ( + + ) : null} +
+ ); +} + +export default () => { return ; }; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx new file mode 100644 index 00000000..2673e1d4 --- /dev/null +++ b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx @@ -0,0 +1,105 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classNameFactory } from "@api/Styles"; +import { classes } from "@utils/misc"; +import { + closeModal, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal, +} from "@utils/modal"; +import { findByPropsLazy } from "@webpack"; +import { Button, Forms, IconUtils, Text, useStateFromStores } from "@webpack/common"; +import { Guild } from "discord-types/general"; + +import { HiddenServersStore } from "../HiddenServersStore"; + +const cl = classNameFactory("vc-hideservers-"); +const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); + +function HiddenServersModal({ + modalProps, + close, +}: { + modalProps: ModalProps; + close(): void; +}) { + const servers = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); + return ( + + + + Hidden Servers + + + + + + + + + ); +} + +export function HiddenServersMenu({ servers }: { servers: Guild[]; }) { + return
+ {servers.length > 0 ? ( + servers.map(server => ( +
+
+ {server.icon + ? + :
+ {server.acronym} +
+ } +
+ + {server.name} + + +
+ )) + ) : ( + + No hidden servers + + )} +
; +} + +export function openHiddenServersModal() { + const key = openModal(modalProps => { + return ( + closeModal(key)} + /> + ); + }); +} diff --git a/src/equicordplugins/hideServers/components/style.css b/src/equicordplugins/hideServers/components/style.css new file mode 100644 index 00000000..ee553841 --- /dev/null +++ b/src/equicordplugins/hideServers/components/style.css @@ -0,0 +1,47 @@ +.vc-hideservers-button-wrapper { + display: flex; + justify-content: center; + margin: 0 0 8px; +} + +.vc-hideservers-button { + max-width: 48px; + font-size: 60%; + background-color: var(--background-primary); + color: var(--header-secondary); +} + +.vc-hideservers-list { + flex: 1 1 auto; +} + +/* copied from blocked row */ +.vc-hideservers-row { + height: 62px; + display: flex; + align-items: center; + flex-direction: row; + margin-left: 20px; + margin-right: 20px; + border-top: 1px solid var(--background-modifier-accent); + border-bottom: 1px solid transparent; + justify-content: space-between; +} + +.vc-hideservers-guildicon { + display: flex; + align-items: center; + margin-right: 1em; +} + +.vc-hideservers-guildicon img { + border-radius: 50%; +} + +.vc-hideservers-name { + flex-grow: 1; +} + +.vc-hideservers-row-button { + margin-left: auto; +} diff --git a/src/equicordplugins/hideServers/index.tsx b/src/equicordplugins/hideServers/index.tsx new file mode 100644 index 00000000..a7dfbd4a --- /dev/null +++ b/src/equicordplugins/hideServers/index.tsx @@ -0,0 +1,141 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// additional thanks to mwittrien/DevilBro and nexpid for their server hiding plugins, which served as inspiration + +import { + findGroupChildrenByChildId, + NavContextMenuPatchCallback, +} from "@api/ContextMenu"; +import { + addServerListElement, + removeServerListElement, + ServerListRenderPosition, +} from "@api/ServerList"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { Menu, React, useStateFromStores } from "@webpack/common"; +import { Guild } from "discord-types/general"; + +import hiddenServersButton from "./components/HiddenServersButton"; +import { HiddenServersStore } from "./HiddenServersStore"; +import settings from "./settings"; + +type guildsNode = { + type: "guild" | "folder"; + id: number | string; + children: guildsNode[]; +}; + +type qsResult = { + type: "GUILD" | string; + record?: { + id?: string; + guild_id?: string; + }; +}; + +const Patch: NavContextMenuPatchCallback = ( + children, + { guild }: { guild: Guild; } +) => { + const group = findGroupChildrenByChildId("privacy", children); + + group?.push( + HiddenServersStore.addHidden(guild)} + /> + ); +}; + +export function addIndicator() { + addServerListElement(ServerListRenderPosition.Below, hiddenServersButton); +} + +export function removeIndicator() { + removeServerListElement(ServerListRenderPosition.Below, hiddenServersButton); +} + +export default definePlugin({ + name: "HideServers", + description: "Allows you to hide servers from the guild list and quick switcher by right clicking them", + authors: [EquicordDevs.bep], + tags: ["guild", "server", "hide"], + + dependencies: ["ServerListAPI"], + contextMenus: { + "guild-context": Patch, + "guild-header-popout": Patch, + }, + patches: [ + { + find: '("guildsnav")', + replacement: [ + { + match: /(?<=Messages\.SERVERS,children:.{0,300}?)(\i)(\)?\.map\(\i\))/, + replace: "$self.useFilteredGuilds($1)$2", + }, + // despite my best efforts, the above doesnt trigger a rerender + { + match: /let{disableAppDownload.{0,10}isPlatformEmbedded/, + replace: "$self.useStore();$&", + } + ] + }, + { + find: "QUICKSWITCHER_PROTIP.format", + replacement: { + match: /(?<=renderResults\(\){)let{query/, + replace: "this.props.results = $self.filteredGuildResults(this.props.results);$&", + }, + }, + ], + settings, + useStore: () => { useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); }, + + async start() { + if (settings.store.showIndicator) { + addIndicator(); + } + await HiddenServersStore.load(); + }, + + async stop() { + removeIndicator(); + HiddenServersStore.unload(); + }, + + useFilteredGuilds(guilds: guildsNode[]): guildsNode[] { + const hiddenGuilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuilds, undefined, (old, newer) => old.size === newer.size); + return guilds.flatMap(guild => { + if (hiddenGuilds.has(guild.id.toString())) { + return []; + } + const newGuild = Object.assign({}, guild); + newGuild.children = guild.children.filter( + child => !hiddenGuilds.has(child.id.toString()) + ); + + return [newGuild]; + }); + }, + + filteredGuildResults(results: qsResult[]): qsResult[] { + // not used in a component so no useStateFromStore + const { hiddenGuilds } = HiddenServersStore; + return results.filter(result => { + if (result?.record?.guild_id && hiddenGuilds.has(result.record.guild_id)) { + return false; + } + if (result.type === "GUILD" && hiddenGuilds.has(result.record!.id!)) { + return false; + } + return true; + }); + }, +}); diff --git a/src/equicordplugins/hideServers/settings.tsx b/src/equicordplugins/hideServers/settings.tsx new file mode 100644 index 00000000..1f684824 --- /dev/null +++ b/src/equicordplugins/hideServers/settings.tsx @@ -0,0 +1,51 @@ +/* + * 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 { Button, useStateFromStores } from "@webpack/common"; + +import { addIndicator, removeIndicator } from "."; +import { HiddenServersMenu } from "./components/HiddenServersMenu"; +import { HiddenServersStore } from "./HiddenServersStore"; + +export default definePluginSettings({ + showIndicator: { + type: OptionType.BOOLEAN, + description: "Show menu to unhide servers at the bottom of the list", + default: true, + onChange: val => { + if (val) { + addIndicator(); + } else { + removeIndicator(); + } + } + }, + guildsList: { + type: OptionType.COMPONENT, + description: "Remove hidden servers", + component: () => { + const detail = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail()); + return ; + } + }, + resetHidden: { + type: OptionType.COMPONENT, + description: "Remove all hidden guilds from the list", + component: () => ( +
+ +
+ ), + }, +}); diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 7904e78b..262def65 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -33,10 +33,16 @@ export default definePlugin({ }, { find: "Messages.SERVERS,children", - replacement: { - match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/, - replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" - } + replacement: [ + { + match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/, + replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" + }, + { + match: /guildDiscoveryRef.{0,300}\}\)\]\}\)\}\)/, + replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" + } + ] } ] }); From c1a8934f6d547ee8b50b7a0ffc693ffde21152ac Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:33:13 -0400 Subject: [PATCH 05/12] Fixes --- .../messageLoggerEnhanced/LoggedMessageManager.ts | 4 ++-- src/plugins/_api/serverList.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts b/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts index 88bad6d6..8b5127ee 100644 --- a/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts +++ b/src/equicordplugins/messageLoggerEnhanced/LoggedMessageManager.ts @@ -32,9 +32,9 @@ export const MessageLoggerStore = createStore("MessageLoggerData", "MessageLogge // this gets used by the logs modal. logs modal should only use saved messages not messages that are being processed // also hasMessageInLogs should only check saved messages not the ones that are being processed -export let savedLoggedMessages: LoggedMessages = defaultLoggedMessages; +export let savedLoggedMessages: LoggedMessages = { ...defaultLoggedMessages }; -export let loggedMessages: LoggedMessages = defaultLoggedMessages; +export let loggedMessages: LoggedMessages = { ...defaultLoggedMessages }; (async () => { try { diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index 262def65..7a12a82c 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -39,7 +39,7 @@ export default definePlugin({ replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" }, { - match: /guildDiscoveryRef.{0,300}\}\)\]\}\)\}\)/, + match: /lastTargetNode.{0,300}\{\}\)\]\}\)\]/, replace: "$&.concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Below))" } ] From 98acff8fc7697d31f2ab58cd9efaf9c797a1fb5c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:33:19 -0400 Subject: [PATCH 06/12] Revert "Update README.md" This reverts commit 7eb6ed000901dbc33f4746dfad0a0a7cb646e1aa. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9fe95c58..ff6cf50a 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Grammar by Samwich - GrammarFix by unstream - HideMessage by Hanzy +- HideServers by bepvte - HolyNotes by Wolfie - HomeTyping by Samwich - HopOn by ImLvna From 416d85dcf096d4265abbe2aa9e89ead74d3e72ab Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:20:06 -0300 Subject: [PATCH 07/12] new plugin FixImagesQuality --- src/plugins/fixImagesQuality/README.md | 3 +++ src/plugins/fixImagesQuality/index.ts | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/plugins/fixImagesQuality/README.md create mode 100644 src/plugins/fixImagesQuality/index.ts diff --git a/src/plugins/fixImagesQuality/README.md b/src/plugins/fixImagesQuality/README.md new file mode 100644 index 00000000..8e3cf654 --- /dev/null +++ b/src/plugins/fixImagesQuality/README.md @@ -0,0 +1,3 @@ +# Fix Images Quality + +Prevents images from being loaded as webp, which can cause quality loss diff --git a/src/plugins/fixImagesQuality/index.ts b/src/plugins/fixImagesQuality/index.ts new file mode 100644 index 00000000..8f84573c --- /dev/null +++ b/src/plugins/fixImagesQuality/index.ts @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "FixImagesQuality", + description: "Prevents images from being loaded as webp, which can cause quality loss", + authors: [Devs.Nuckyz], + patches: [ + { + find: "getFormatQuality(){", + replacement: { + match: /(?<=null;return )\i\.\i&&\(\i\|\|!\i\.isAnimated.+?:(?=\i&&\(\i="png"\))/, + replace: "" + } + } + ] +}); From 94831de71114425eb403da65dcff4dce345af8a4 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:33:56 -0400 Subject: [PATCH 08/12] Fix DeadMembers --- src/equicordplugins/deadMembers/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/deadMembers/index.tsx b/src/equicordplugins/deadMembers/index.tsx index d14df454..7e253de4 100644 --- a/src/equicordplugins/deadMembers/index.tsx +++ b/src/equicordplugins/deadMembers/index.tsx @@ -17,8 +17,8 @@ export default definePlugin({ { find: '.BADGES=1]="BADGES"', replacement: { - match: /(\i)=\{className:\i.username,style:.*?onContextMenu:\i,children:.*?\};/, - replace: "$&$1.children=$self.wrapMessageAuthor(arguments[0],$1.children);" + match: /(\i)=\{className:\i.username,style:.*?onContextMenu:\i,children:.*?\},/, + replace: "$&__dummyvar=($1.children=$self.wrapMessageAuthor(arguments[0],$1.children))," } }, { From 77be17dcce5c9df8dbf800f7e9318f8319db9239 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:42:21 -0400 Subject: [PATCH 09/12] Update LoginWithQR --- src/equicordplugins/loginWithQR/images.ts | 6 +- src/equicordplugins/loginWithQR/index.tsx | 76 ++++++++++--------- src/equicordplugins/loginWithQR/ui/index.ts | 1 - .../loginWithQR/ui/modals/QrModal.tsx | 26 ++++++- src/equicordplugins/loginWithQR/ui/styles.css | 29 +++++-- 5 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/equicordplugins/loginWithQR/images.ts b/src/equicordplugins/loginWithQR/images.ts index c73d3e98..865ea50a 100644 --- a/src/equicordplugins/loginWithQR/images.ts +++ b/src/equicordplugins/loginWithQR/images.ts @@ -8,11 +8,11 @@ export const images = { cross: "https://i.imgur.com/XxRnu3b.png", deviceImage: { success: - "https://github.com/nexpid/Themelings/blob/3a063c5188f4cac096171f29163f9e2659f275a3/icons/images/native/img_remote_auth_succeeded.png", + "https://github.com/nexpid/Themelings/raw/data/icons/images/native/img_remote_auth_succeeded.png", notFound: - "https://github.com/nexpid/Themelings/blob/3a063c5188f4cac096171f29163f9e2659f275a3/icons/images/native/img_remote_auth_not_found.png", + "https://github.com/nexpid/Themelings/raw/data/icons/images/native/img_remote_auth_not_found.png", loading: - "https://github.com/nexpid/Themelings/blob/3a063c5188f4cac096171f29163f9e2659f275a3/icons/images/native/img_remote_auth_loaded.png", + "https://github.com/nexpid/Themelings/raw/data/icons/images/native/img_remote_auth_loaded.png", }, } as const; diff --git a/src/equicordplugins/loginWithQR/index.tsx b/src/equicordplugins/loginWithQR/index.tsx index 394165ac..bd2653b6 100644 --- a/src/equicordplugins/loginWithQR/index.tsx +++ b/src/equicordplugins/loginWithQR/index.tsx @@ -5,19 +5,25 @@ */ import { definePluginSettings } from "@api/Settings"; -import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { Button, Forms, i18n, Menu, TabBar } from "@webpack/common"; +import { Button, Forms, i18n, Menu } from "@webpack/common"; import { ReactElement } from "react"; import { preload, unload } from "./images"; -import { cl, QrCodeIcon } from "./ui"; +import { cl } from "./ui"; import openQrModal from "./ui/modals/QrModal"; export default definePlugin({ name: "LoginWithQR", - description: "Allows you to login to another device by scanning a login QR code, just like on mobile!", - authors: [EquicordDevs.nexpid], + description: + "Allows you to login to another device by scanning a login QR code, just like on mobile!", + // replace with EquicordDevs.nexpid when merged to Equicord + authors: [ + { + name: "Nexpid", + id: 853550207039832084n, + }, + ], settings: definePluginSettings({ scanQr: { @@ -62,25 +68,31 @@ export default definePlugin({ replace: ",$self.insertScanQrButton($1)", }, }, - // Insert a Scan QR Code MenuItem in the simplified user popout + // Insert a Scan QR Code MenuItem in the Swith Accounts popout { - find: "Messages.MULTI_ACCOUNT_MENU_LABEL", + find: ".SWITCH_ACCOUNTS_MANAGE_ACCOUNTS,", replacement: { - // Insert our own MenuItem before the Switch Accounts button - match: /children:\[(.{0,54}id:"switch-accounts")/, - replace: "children:[$self.ScanQrMenuItem,$1", - }, + match: /(id:"manage-accounts",.*?)}\)\)(,\i)/, + replace: "$1}),$self.ScanQrMenuItem)$2" + } }, - // Add a Scan QR entry to the settings TabBar + + // Insert a Scan QR Code button in the Settings sheet { - find: ".BILLING_SETTINGS,", + find: "useGenerateUserSettingsSections", replacement: { - match: /((\i\.settings)\.forEach.+?(\i).push\(.+}\)}\))/, - replace: (_, original, settings, elements) => - `${original},${settings}?.[0]=="ACCOUNT"` + - `&&${elements}.push({section:"CUSTOM",element:$self.ScanQrTabBarComponent})`, - }, + match: /(\.FRIEND_REQUESTS)/, + replace: "$1,\"SCAN_QR_CODE\"" + } }, + // Insert a Scan QR Code button in the Settings sheet (part 2) + { + find: ".PRIVACY_ENCRYPTION_VERIFIED_DEVICES_V2]", + replacement: { + match: /(\.CLIPS]:{.*?},)/, + replace: "$1\"SCAN_QR_CODE\":$self.ScanQrSettingsSheet," + } + } ], qrModalOpen: false, @@ -93,26 +105,18 @@ export default definePlugin({ {button} ), - get ScanQrMenuItem() { - return ( - - - - ); + return ; + }, + get ScanQrSettingsSheet() { + return { + section: i18n.Messages.USER_SETTINGS_SCAN_QR_CODE, + onClick: openQrModal, + searchableTitles: [i18n.Messages.USER_SETTINGS_SCAN_QR_CODE], + label: i18n.Messages.USER_SETTINGS_SCAN_QR_CODE, + ariaLabel: i18n.Messages.USER_SETTINGS_SCAN_QR_CODE + }; }, - - ScanQrTabBarComponent: () => ( - - {i18n.Messages.USER_SETTINGS_SCAN_QR_CODE} - - ), start() { // Preload images diff --git a/src/equicordplugins/loginWithQR/ui/index.ts b/src/equicordplugins/loginWithQR/ui/index.ts index e2bc34cc..8ee835d5 100644 --- a/src/equicordplugins/loginWithQR/ui/index.ts +++ b/src/equicordplugins/loginWithQR/ui/index.ts @@ -43,4 +43,3 @@ export const QrCodeIcon = proxyLazy(() => icons.QrCodeCameraIcon ?? icons.QrCode }>; export const cl = classNameFactory("qrlogin-"); - diff --git a/src/equicordplugins/loginWithQR/ui/modals/QrModal.tsx b/src/equicordplugins/loginWithQR/ui/modals/QrModal.tsx index efa3ba64..2cd87a73 100644 --- a/src/equicordplugins/loginWithQR/ui/modals/QrModal.tsx +++ b/src/equicordplugins/loginWithQR/ui/modals/QrModal.tsx @@ -345,6 +345,7 @@ function QrModal(props: ModalProps) { video.srcObject = str; video.addEventListener("loadedmetadata", () => { if (stopped) return stop(str); + video.play(); modalProps.current.setPreview(video); snapshot(); @@ -381,7 +382,8 @@ function QrModal(props: ModalProps) { state === LoginStateType.Camera && !preview?.source && "modal-filepaste-disabled", - preview?.source && "modal-filepaste-preview" + preview?.source && "modal-filepaste-preview", + preview?.crosses && "modal-filepaste-crosses" )} onClick={() => state === LoginStateType.Idle && inputRef.current?.click() @@ -418,10 +420,28 @@ function QrModal(props: ModalProps) { > {preview?.source ? (
i + x, 0) / + preview.crosses.length - + 50)}%` + : undefined, + ["--offset-y" as any]: preview.crosses + ? `${-(preview.crosses.reduce((i, { y }) => i + y, 0) / + preview.crosses.length - + 50)}%` + : undefined, + }} + className={cl(preview?.crosses && "preview-crosses")} > {preview.source} - {preview.crosses?.map(({ x, y, rot, size }, i) => ( + {preview.crosses?.map(({ x, y, rot, size }) => ( Date: Mon, 7 Oct 2024 18:11:29 +0300 Subject: [PATCH 10/12] Add icon to userscript meta (#2936) --- browser/userscript.meta.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/userscript.meta.js b/browser/userscript.meta.js index 5b2a39be..1d986aae 100644 --- a/browser/userscript.meta.js +++ b/browser/userscript.meta.js @@ -5,6 +5,7 @@ // @author Vendicated (https://github.com/Vendicated) // @namespace https://github.com/Vendicated/Vencord // @supportURL https://github.com/Vendicated/Vencord +// @icon https://raw.githubusercontent.com/Vendicated/Vencord/refs/heads/main/browser/icon.png // @license GPL-3.0 // @match *://*.discord.com/* // @grant GM_xmlhttpRequest From ef74368a2f6084ae1a0d40e6b63384179791c7ea Mon Sep 17 00:00:00 2001 From: Creation's Date: Tue, 8 Oct 2024 12:01:51 -0400 Subject: [PATCH 11/12] feat(plugin): ImagePreview (#47) * add imagePreview plugin, add myself to EquicordDevs constant, and update the readme * Update README.md update additional plugin count * Fix * Fix Dir Name * Fix My Mistake --------- Co-authored-by: thororen1234 Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- README.md | 3 +- package.json | 2 +- src/equicordplugins/imagePreview/index.ts | 397 ++++++++++++++++++++ src/equicordplugins/imagePreview/styles.css | 63 ++++ src/equicordplugins/loginWithQR/index.tsx | 12 +- src/utils/constants.ts | 4 + 6 files changed, 470 insertions(+), 11 deletions(-) create mode 100644 src/equicordplugins/imagePreview/index.ts create mode 100644 src/equicordplugins/imagePreview/styles.css diff --git a/README.md b/README.md index ff6cf50a..5577fad1 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
-125 additional plugins +126 additional plugins - AllCallTimers by MaxHerbold & D3SOX - AltKrispSwitch by newwares @@ -67,6 +67,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 - Jumpscare by Surgedevs - JumpToStart by Samwich - KeyboardSounds by HypedDomi diff --git a/package.json b/package.json index 5ee0b6c0..77396d5b 100644 --- a/package.json +++ b/package.json @@ -117,4 +117,4 @@ "node": ">=18", "pnpm": ">=9" } -} +} \ No newline at end of file diff --git a/src/equicordplugins/imagePreview/index.ts b/src/equicordplugins/imagePreview/index.ts new file mode 100644 index 00000000..f78d9128 --- /dev/null +++ b/src/equicordplugins/imagePreview/index.ts @@ -0,0 +1,397 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { definePluginSettings } from "@api/Settings"; + +const eventListeners: { element: HTMLElement, handler: (e: any) => void; }[] = []; +let lastHoveredElement: HTMLElement | null = null; + +const mimeTypes = { + jpg: "image/jpeg", + jpeg: "image/jpeg", + png: "image/png", + gif: "image/gif", + webp: "image/webp", + svg: "image/svg+xml", + mp4: "video/mp4", + webm: "video/webm", + mov: "video/quicktime", +}; + +function getMimeType(extension: string | undefined): [boolean, string] { + if (!extension) return [false, ""]; + + const lowerExt = extension.trim().toLowerCase(); + return [!!mimeTypes[lowerExt], mimeTypes[lowerExt] || ""]; +} + +function addHoverEffect(element: HTMLElement, type: string) { + let hoverElementActual; + + if (settings.store.hoverOutline) { + if (type === "messageImages") { + hoverElementActual = element.closest('[id^="message-accessories-"]')?.querySelector('div')?.querySelector('div') || element; + + if (!(hoverElementActual instanceof HTMLDivElement)) { + hoverElementActual = element; + } + } else { + hoverElementActual = element.querySelector("img") || element; + } + + hoverElementActual.style.outline = `${settings.store.hoverOutlineSize} dotted ${settings.store.hoverOutlineColor}`; + } + + const url = element.getAttribute("data-safe-src") || element.getAttribute("src") || element.getAttribute("href") || element.textContent; + + if (!url) { + hoverElementActual.style.outline = ""; + return; + } + + const strippedUrl = stripDiscordParams(url); + const fileName: string = strippedUrl.split("/").pop()?.split(/[?#&]/)[0] || "unknown"; + const [allowed, mimeType] = getMimeType(fileName.split(".").pop()); + + if (!allowed) { + hoverElementActual.style.outline = ""; + return; + } + + const isImage = allowed && mimeType.startsWith("image"); + const isVideo = allowed && mimeType.startsWith("video"); + + const previewDiv = document.createElement("div"); + previewDiv.classList.add("preview-div"); + + let mediaElement; + if (isImage) { + mediaElement = document.createElement("img"); + mediaElement.src = strippedUrl; + mediaElement.alt = fileName; + } else if (isVideo) { + mediaElement = document.createElement("video"); + mediaElement.src = strippedUrl; + mediaElement.autoplay = true; + mediaElement.muted = true; + mediaElement.loop = true; + mediaElement.controls = false; + } else { + return; + } + + const previewHeader = document.createElement("div"); + previewHeader.classList.add("preview-header"); + + const mimeSpan = document.createElement("span"); + mimeSpan.textContent = `MIME: ${mimeType}`; + previewHeader.appendChild(mimeSpan); + + const fileNameSpan = document.createElement("span"); + fileNameSpan.classList.add("file-name"); + fileNameSpan.textContent = fileName; + previewHeader.appendChild(fileNameSpan); + + const dimensionsDiv = document.createElement("div"); + dimensionsDiv.classList.add("dimensions-div"); + + const dimensionsDisplaying = document.createElement("span"); + dimensionsDisplaying.classList.add("dimensions-displaying"); + const dimensionsOriginal = document.createElement("span"); + dimensionsOriginal.classList.add("dimensions-original"); + + mediaElement.onload = mediaElement.onloadstart = () => { + if (isImage) { + dimensionsDisplaying.textContent = `Displaying: ${mediaElement.width}x${mediaElement.height}`; + dimensionsOriginal.textContent = `Original: ${mediaElement.naturalWidth}x${mediaElement.naturalHeight}`; + } else if (isVideo) { + dimensionsDisplaying.textContent = `Displaying: ${mediaElement.videoWidth}x${mediaElement.videoHeight}`; + dimensionsOriginal.textContent = `Original: ${mediaElement.videoWidth}x${mediaElement.videoHeight}`; + } + + if (mediaElement.width < 200) { + previewHeader.style.flexDirection = "column"; + previewHeader.style.alignItems = "center"; + previewHeader.style.justifyContent = "center"; + dimensionsDiv.style.textAlign = "center"; + dimensionsDiv.style.alignItems = "center"; + previewHeader.style.gap = "5px"; + previewHeader.insertBefore(fileNameSpan, previewHeader.firstChild); + } + }; + + dimensionsDiv.appendChild(dimensionsDisplaying); + dimensionsDiv.appendChild(dimensionsOriginal); + previewHeader.appendChild(dimensionsDiv); + + previewDiv.appendChild(previewHeader); + previewDiv.appendChild(mediaElement); + + document.body.appendChild(previewDiv); + + const hoverDelay = settings.store.hoverDelay * 1000; + let timeout; + + const showPreview = () => { + timeout = setTimeout(() => { + previewDiv.style.display = "block"; + hoverElementActual.style.outline = `${settings.store.hoverOutlineSize} dotted ${settings.store.hoverOutlineColor}`; + positionPreviewDiv(previewDiv, null); + }, hoverDelay); + }; + + const movePreviewListener: (e: MouseEvent) => void = (e) => { + positionPreviewDiv(previewDiv, e); + }; + + const removePreview = () => { + clearTimeout(timeout); + previewDiv.remove(); + document.removeEventListener("mousemove", movePreviewListener); + if (hoverElementActual) { + hoverElementActual.style.outline = ""; + } + }; + + element.addEventListener("mouseenter", showPreview); + element.addEventListener("mouseleave", removePreview); + document.addEventListener("mousemove", movePreviewListener); + + eventListeners.push({ element, handler: showPreview }); + eventListeners.push({ element, handler: removePreview }); + eventListeners.push({ element: previewDiv, handler: movePreviewListener }); + + function positionPreviewDiv(previewDiv: HTMLElement, e: MouseEvent | null) { + const previewWidth = previewDiv.offsetWidth; + const previewHeight = previewDiv.offsetHeight; + const pageWidth = window.innerWidth; + const pageHeight = window.innerHeight; + + const mouseX = e ? e.pageX : window.innerWidth / 2; + const mouseY = e ? e.pageY : window.innerHeight / 2; + + let left = mouseX + 10; + let top = mouseY + 10; + + if (left + previewWidth > pageWidth) { + left = pageWidth - previewWidth - 10; + } + if (top + previewHeight > pageHeight) { + top = pageHeight - previewHeight - 10; + } + + previewDiv.style.left = `${left}px`; + previewDiv.style.top = `${top}px`; + + const maxImageWidth = pageWidth - 20; + const maxImageHeight = pageHeight - 20; + + if (isImage) { + if (mediaElement.naturalWidth > maxImageWidth || mediaElement.naturalHeight > maxImageHeight) { + const aspectRatio = mediaElement.naturalWidth / mediaElement.naturalHeight; + + if (mediaElement.naturalWidth > maxImageWidth) { + mediaElement.width = maxImageWidth; + mediaElement.height = maxImageWidth / aspectRatio; + } + if (mediaElement.height > maxImageHeight) { + mediaElement.height = maxImageHeight; + mediaElement.width = maxImageHeight * aspectRatio; + } + } else { + mediaElement.width = mediaElement.naturalWidth; + mediaElement.height = mediaElement.naturalHeight; + } + } + + dimensionsDisplaying.textContent = isImage ? `Displaying: ${mediaElement.width}x${mediaElement.height}` : `Displaying: ${mediaElement.videoWidth}x${mediaElement.videoHeight}`; + dimensionsOriginal.textContent = isImage ? `Original: ${mediaElement.naturalWidth}x${mediaElement.naturalHeight}` : `Original: ${mediaElement.videoWidth}x${mediaElement.videoHeight}`; + } +} + +function handleHover(elements: NodeListOf | HTMLElement[], type: string) { + elements.forEach((el) => { + if (!el.dataset.hoverListenerAdded) { + const handler = () => addHoverEffect(el, type); + el.addEventListener("mouseover", handler); + el.dataset.hoverListenerAdded = "true"; + eventListeners.push({ element: el, handler }); + } + }); +} + +function isLinkAnImage(url: string) { + const extension = url.split(".").pop(); + const [isImage,] = getMimeType(extension); + return isImage; +} + +function stripDiscordParams(url: string) { + let newUrl = url.replace(/([?&])(width|size|height|h|w)=[^&]+/g, ""); + + newUrl = newUrl.replace(/([?&])quality=[^&]*/g, "$1quality=lossless"); + + newUrl = newUrl.replace(/([?&])+$/, "") + .replace(/\?&/, "?") + .replace(/\?$/, "") + .replace(/&{2,}/g, "&"); + + if (newUrl.includes("quality=lossless") && !newUrl.includes("?")) { + newUrl = newUrl.replace(/&quality=lossless/, "?quality=lossless"); + } + + return newUrl; +} + +const settings = definePluginSettings({ + messageImages: { + type: OptionType.BOOLEAN, + description: "Enable Message Images Hover Detection", + default: true, + }, + messageAvatars: { + type: OptionType.BOOLEAN, + description: "Enable Message Avatars Hover Detection", + default: true, + }, + messageLinks: { + type: OptionType.BOOLEAN, + description: "Enable Message Links Hover Detection", + default: true, + }, + messageStickers: { + type: OptionType.BOOLEAN, + description: "Enable Message Stickers Hover Detection", + default: true, + }, + hoverOutline: { + type: OptionType.BOOLEAN, + description: "Enable Hover Outline on Elements", + default: true, + }, + hoverOutlineColor: { + type: OptionType.STRING, + description: "Hover Outline Color", + default: "red", + }, + hoverOutlineSize: { + type: OptionType.STRING, + description: "Hover Outline Size", + default: "1px", + }, + hoverDelay: { + type: OptionType.SLIDER, + description: "Display Hover Delay (seconds)", + markers: [0, 1, 2, 3, 4, 5], + default: 1, + }, +}); + +export default definePlugin({ + name: "Image Preview", + description: "Hover on images, avatars, links, guild icons, and stickers to show a full preview.", + authors: [EquicordDevs.creations], + settings: settings, + + start() { + function initialScan() { + const appContainer = document.querySelector('[class*="app-"]'); + if (appContainer) { + if (settings.store.messageImages) { + handleHover(appContainer.querySelectorAll('[data-role="img"]'), "messageImages"); + } + + if (settings.store.messageAvatars) { + handleHover(appContainer.querySelectorAll('img[src*="cdn.discordapp.com/avatars/"], img[src*="cdn.discordapp.com/guilds/"], img[src^="/assets/"][class*="avatar"]'), "messageAvatars"); + } + + if (settings.store.messageLinks) { + appContainer.querySelectorAll("span").forEach((span) => { + const url = span.textContent?.replace(/<[^>]*>?/gm, ""); + if (url && (url.startsWith("http://") || url.startsWith("https://")) && isLinkAnImage(url)) { + handleHover([span], "messageLinks"); + } + }); + } + + if (settings.store.messageStickers) { + handleHover(appContainer.querySelectorAll('img[data-type="sticker"]'), "messageStickers"); + } + } + } + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === "childList") { + mutation.addedNodes.forEach((addedNode) => { + if (addedNode instanceof HTMLElement) { + const element = addedNode as HTMLElement; + + if (lastHoveredElement === element) return; + lastHoveredElement = element; + + if (settings.store.messageImages) { + handleHover(element.querySelectorAll('[data-role="img"]'), "messageImages"); + } + + if (settings.store.messageAvatars) { + handleHover(element.querySelectorAll('img[src*="cdn.discordapp.com/avatars/"], img[src*="cdn.discordapp.com/guilds/"], img[src^="/assets/"][class*="avatar"]'), "messageAvatars"); + } + + if (settings.store.messageLinks) { + element.querySelectorAll("span").forEach((span) => { + const url = span.textContent?.replace(/<[^>]*>?/gm, ""); + if (url && (url.startsWith("http://") || url.startsWith("https://")) && isLinkAnImage(url)) { + handleHover([span], "messageLinks"); + } + }); + } + + if (settings.store.messageStickers) { + handleHover(element.querySelectorAll('img[data-type="sticker"]'), "messageStickers"); + } + } + }); + } + }); + }); + + const appContainer = document.querySelector('[class*="app-"]'); + if (appContainer) { + observer.observe(appContainer, { childList: true, subtree: true }); + } + + initialScan(); + + this.observer = observer; + }, + + stop() { + this.observer.disconnect(); + + eventListeners.forEach(({ element, handler }) => { + element.removeEventListener("mouseover", handler); + element.removeEventListener("mouseenter", handler); + element.removeEventListener("mouseleave", handler); + element.removeEventListener("mousemove", handler); + }); + + eventListeners.length = 0; + + document.querySelectorAll("[data-hover-listener-added]").forEach((el) => { + el.removeAttribute("data-hover-listener-added"); + (el as HTMLElement).style.outline = ""; + }); + + document.querySelectorAll(".preview-div").forEach((preview) => { + preview.remove(); + }); + } +}); diff --git a/src/equicordplugins/imagePreview/styles.css b/src/equicordplugins/imagePreview/styles.css new file mode 100644 index 00000000..e3dee0d2 --- /dev/null +++ b/src/equicordplugins/imagePreview/styles.css @@ -0,0 +1,63 @@ +.preview-div { + position: fixed; + border: 2px solid var(--background-tertiary); + background-color: var(--background-secondary); + padding: 5px; + border-radius: 5px; + pointer-events: none; + z-index: 9999; + display: none; + max-width: 100vw; + max-height: 96vh; + overflow: hidden; + box-shadow: 0 4px 20px rgba(0 0 0 / 40%); +} + +.file-name { + font-size: 15px; + color: var(--text-normal); + text-align: center; + margin-bottom: 5px; + display: block; + word-break: break-all; + max-width: 180px; +} + +.preview-div img { + max-width: 100%; + max-height: 100%; + display: block; + margin: 0 auto; +} + +.preview-div video { + max-width: 100%; + max-height: 100%; + display: block; + margin: 0 auto; +} + +.preview-header { + color: white; + font-size: 12px; + margin-bottom: 5px; + text-align: center; + display: flex; + justify-content: space-between; + align-items: center; + gap: 5px; +} + +.dimensions-div { + display: flex; + flex-direction: column; + gap: 5px; + align-items: flex-end; + font-size: 11px; + color: var(--text-muted); +} + +.dimensions-displaying, +.dimensions-original { + display: block; +} diff --git a/src/equicordplugins/loginWithQR/index.tsx b/src/equicordplugins/loginWithQR/index.tsx index bd2653b6..dbadb292 100644 --- a/src/equicordplugins/loginWithQR/index.tsx +++ b/src/equicordplugins/loginWithQR/index.tsx @@ -5,6 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { Button, Forms, i18n, Menu } from "@webpack/common"; import { ReactElement } from "react"; @@ -15,15 +16,8 @@ import openQrModal from "./ui/modals/QrModal"; export default definePlugin({ name: "LoginWithQR", - description: - "Allows you to login to another device by scanning a login QR code, just like on mobile!", - // replace with EquicordDevs.nexpid when merged to Equicord - authors: [ - { - name: "Nexpid", - id: 853550207039832084n, - }, - ], + description: "Allows you to login to another device by scanning a login QR code, just like on mobile!", + authors: [EquicordDevs.nexpid], settings: definePluginSettings({ scanQr: { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 04479b28..ced902cb 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -913,6 +913,10 @@ export const EquicordDevs = Object.freeze({ name: "arHSM", id: 841509053422632990n }, + creations: { + name: "Creation's", + id: 209830981060788225n, + } } satisfies Record); // iife so #__PURE__ works correctly From b79be8746569640a5a08ee8ee31575797eff5029 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:03:28 -0400 Subject: [PATCH 12/12] ... --- src/plugins/fixImagesQuality/README.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/plugins/fixImagesQuality/README.md diff --git a/src/plugins/fixImagesQuality/README.md b/src/plugins/fixImagesQuality/README.md deleted file mode 100644 index 8e3cf654..00000000 --- a/src/plugins/fixImagesQuality/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Fix Images Quality - -Prevents images from being loaded as webp, which can cause quality loss