From 1dba51a5482f7ed21bcc54ec7089ffce698bbd6f Mon Sep 17 00:00:00 2001 From: Rico040 <93081680+Rico040@users.noreply.github.com> Date: Tue, 21 May 2024 22:45:04 +1000 Subject: [PATCH] feat(`Always Animate`): at least made it enableable --- plugins/always-animate/manifest.json | 18 ++++++ plugins/always-animate/src/index.ts | 92 ++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 plugins/always-animate/manifest.json create mode 100644 plugins/always-animate/src/index.ts diff --git a/plugins/always-animate/manifest.json b/plugins/always-animate/manifest.json new file mode 100644 index 0000000..aa61079 --- /dev/null +++ b/plugins/always-animate/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Always Animate", + "description": "Always animates guild icons and avatars.", + "authors": [ + { + "name": "Cynosphere", + "id": "150745989836308480" + }, + { + "name": "Rico040", + "id": "619474349845643275" + } + ], + "main": "src/index.ts", + "vendetta": { + "icon": "icon-qs-gifs" + } +} \ No newline at end of file diff --git a/plugins/always-animate/src/index.ts b/plugins/always-animate/src/index.ts new file mode 100644 index 0000000..a5893fc --- /dev/null +++ b/plugins/always-animate/src/index.ts @@ -0,0 +1,92 @@ +import {before, after} from "@vendetta/patcher"; +import {findByName, findByProps} from "@vendetta/metro"; +import {findInReactTree} from "@vendetta/utils"; + +const Avatar = findByProps("getStatusSize"); +const DisplayBanner = findByName("DisplayBanner", false); +const ImageResolver = findByProps("getAvatarDecorationURL", "default"); +const RowManager = findByName("RowManager"); + +const patches: Function[] = []; + +export const onLoad = () => { + // Guild Icons + if (typeof findByName("GuildIcon").prototype.render !== "undefined") { + const GuildIcon = findByName("GuildIcon"); + patches.push( + before("render", GuildIcon.prototype, function () { + this.props.animate = true; + }) + ); + } else { + const GuildIcon = findByName("GuildIcon", false); + patches.push( + before("default", GuildIcon, ([props]) => { + props.animate = true; + }) + ); + } + + // Avatars (not used by chat) + patches.push( + before("type", Avatar.default, function ([props]) { + props.animate = true; + }) + ); + + // Profile Banners (bypasses GIF playback option) + patches.push( + after("default", DisplayBanner, function (args, ret) { + const ClickWrapperProps = findInReactTree( + ret, + (x) => x.accessibilityRole == "image" && x.onPress != null + ); + const Banner = findInReactTree( + ClickWrapperProps, + (x) => x.type?.name == "ProfileBanner" + ); + if ( + Banner && + Banner.key.endsWith("-false") && + Banner.props.bannerSource?.uri?.indexOf("/a_") > -1 + ) { + ClickWrapperProps.onPress(); + } + }) + ); + + // Catch-all + patches.push( + before("getAvatarDecorationURL", ImageResolver, ([props]) => { + props.canAnimate = true; + }) + ); + patches.push( + before("getUserAvatarURL", ImageResolver, (args) => { + args[1] = true; + }) + ); + patches.push( + before("getGuildMemberAvatarURLSimple", ImageResolver, ([props]) => { + props.canAnimate = true; + }) + ); + + // Chat (iOS only?) + patches.push( + after("generate", RowManager.prototype, ([row], ret) => { + if (row.rowType !== 1) return; + + const {message} = ret; + if (message.avatarURL?.indexOf("a_") > -1) { + message.avatarURL = message.avatarURL.replace(".webp", ".gif"); + } + }) + ); +}; + +export const onUnload = () => { + for (const unpatch of patches) { + unpatch?.(); + } +}; \ No newline at end of file