From 5148fdb15f2a9c97edd5928565e04defe126133e Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:35:59 -0400 Subject: [PATCH] [UI > QuickInstall] Implement (#45) * [UI > QuickInstall] Init * [Constants] Update PROXY_PREFIX * [TS] Type additional constants * [Style] Move Metro filter imports to top of imports --- src/def.d.ts | 5 +++ src/index.ts | 2 + src/lib/constants.ts | 7 +++- src/ui/quickInstall/forumPost.tsx | 65 +++++++++++++++++++++++++++++++ src/ui/quickInstall/index.ts | 11 ++++++ src/ui/quickInstall/url.ts | 29 ++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/ui/quickInstall/forumPost.tsx create mode 100644 src/ui/quickInstall/index.ts create mode 100644 src/ui/quickInstall/url.ts diff --git a/src/def.d.ts b/src/def.d.ts index 90e9e98..19a8083 100644 --- a/src/def.d.ts +++ b/src/def.d.ts @@ -369,8 +369,13 @@ interface VendettaObject { }; constants: { DISCORD_SERVER: string; + DISCORD_SERVER_ID: string; + PLUGINS_CHANNEL_ID: string; + THEMES_CHANNEL_ID: string; GITHUB: string; + PROXY_PREFIX: string; HTTP_REGEX: RegExp; + HTTP_REGEX_MULTI: RegExp; }; utils: { findInReactTree: (tree: SearchTree, filter: SearchFilter) => any; diff --git a/src/index.ts b/src/index.ts index 1ef2bc8..c1e0e5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import { patchLogHook } from "@lib/debug"; import { patchCommands } from "@lib/commands"; import { initPlugins } from "@lib/plugins"; import { patchAssets } from "@ui/assets"; +import initQuickInstall from "@ui/quickInstall"; import initSettings from "@ui/settings"; import initFixes from "@lib/fixes"; import logger from "@lib/logger"; @@ -15,6 +16,7 @@ export default async () => { patchCommands(), initFixes(), initSettings(), + initQuickInstall(), ]); // Assign window object diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 2fca598..4f1b51d 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,3 +1,8 @@ export const DISCORD_SERVER = "https://discord.gg/n9QQ4XhhJP"; +export const DISCORD_SERVER_ID = "1015931589865246730"; +export const PLUGINS_CHANNEL_ID = "1091880384561684561"; +export const THEMES_CHANNEL_ID = "1091880434939482202"; export const GITHUB = "https://github.com/vendetta-mod"; -export const HTTP_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/; \ No newline at end of file +export const PROXY_PREFIX = "https://vd-plugins.github.io/proxy"; +export const HTTP_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/; +export const HTTP_REGEX_MULTI = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g; \ No newline at end of file diff --git a/src/ui/quickInstall/forumPost.tsx b/src/ui/quickInstall/forumPost.tsx new file mode 100644 index 0000000..96789f2 --- /dev/null +++ b/src/ui/quickInstall/forumPost.tsx @@ -0,0 +1,65 @@ +import { findByName, findByProps } from "@metro/filters"; +import { DISCORD_SERVER_ID, PLUGINS_CHANNEL_ID, THEMES_CHANNEL_ID, HTTP_REGEX_MULTI, PROXY_PREFIX } from "@lib/constants"; +import { after } from "@lib/patcher"; +import { installPlugin } from "@lib/plugins"; +import { installTheme } from "@lib/themes"; +import { findInReactTree } from "@lib/utils"; +import { getAssetIDByName } from "@ui/assets"; +import { Forms } from "@ui/components"; +import { showToast } from "@ui/toasts"; + +const ForumPostLongPressActionSheet = findByName("ForumPostLongPressActionSheet", false); +const { FormRow } = Forms; +// Discord uses this Icon in action sheets. FormRow.Icon is too dark. +const Icon = findByName("Icon"); + +const { useFirstForumPostMessage } = findByProps("useFirstForumPostMessage"); +const { hideActionSheet } = findByProps("openLazy", "hideActionSheet"); + +export default () => after("default", ForumPostLongPressActionSheet, ([{ thread }], res) => { + if (thread.guild_id !== DISCORD_SERVER_ID) return; + + // Determine what type of addon this is. + let postType: string; + if (thread.parent_id === PLUGINS_CHANNEL_ID) { + postType = "Plugin"; + } else if (thread.parent_id === THEMES_CHANNEL_ID) { + postType = "Theme"; + } else return; + + const { firstMessage } = useFirstForumPostMessage(thread); + + let urls = firstMessage?.content?.match(HTTP_REGEX_MULTI); + if (!urls) return; + + if (postType === "Plugin") { + urls = urls.filter((url: string) => url.startsWith(PROXY_PREFIX)); + } else { + urls = urls.filter((url: string) => url.endsWith(".json")); + }; + + const url = urls[0]; + if (!url) return; + + /* Assuming that the actions array is at index 1 + could break in the future, but I doubt Discord + will add more to the post action sheet and + index 0 will either be quick add reactions or false. + */ + const actions = findInReactTree(res, (t) => t.props?.bottom === true).props.children.props.children[1]; + const ActionsSection = actions[0].type; + + actions.unshift( + } + label={`Install ${postType}`} + onPress={() => + (postType === "Plugin" ? installPlugin : installTheme)(url).then(() => { + showToast(`Successfully installed ${thread.name}`, getAssetIDByName("Check")); + }).catch((e: Error) => { + showToast(e.message, getAssetIDByName("Small")); + }).finally(() => hideActionSheet()) + } + /> + ); +}); \ No newline at end of file diff --git a/src/ui/quickInstall/index.ts b/src/ui/quickInstall/index.ts new file mode 100644 index 0000000..f3dd1bd --- /dev/null +++ b/src/ui/quickInstall/index.ts @@ -0,0 +1,11 @@ +import patchForumPost from "@ui/quickInstall/forumPost"; +import patchUrl from "@ui/quickInstall/url"; + +export default function initQuickInstall() { + const patches = new Array; + + patches.push(patchForumPost()); + patches.push(patchUrl()); + + return () => patches.forEach(p => p()); +}; \ No newline at end of file diff --git a/src/ui/quickInstall/url.ts b/src/ui/quickInstall/url.ts new file mode 100644 index 0000000..cb379c5 --- /dev/null +++ b/src/ui/quickInstall/url.ts @@ -0,0 +1,29 @@ +import { findByProps } from "@metro/filters"; +import { PROXY_PREFIX } from "@lib/constants"; +import { after } from "@lib/patcher"; +import { installPlugin } from "@lib/plugins"; +import { installTheme } from "@lib/themes"; +import { getAssetIDByName } from "@ui/assets"; +import { showToast } from "@ui/toasts"; + +const showSimpleActionSheet = findByProps("showSimpleActionSheet"); + +export default () => after("showSimpleActionSheet", showSimpleActionSheet, ([{ key, header: { title: url }, options }]) => { + if (key !== "LongPressUrl") return; + + let urlType: string; + if (url.startsWith(PROXY_PREFIX)) { + urlType = "Plugin"; + } else if (url.endsWith(".json")) { + urlType = "Theme"; + } else return; + + options.push({ + label: `Install ${urlType}`, onPress: () => + (urlType === "Plugin" ? installPlugin : installTheme)(url).then(() => { + showToast("Successfully installed", getAssetIDByName("Check")); + }).catch((e: Error) => { + showToast(e.message, getAssetIDByName("Small")); + }), + }); +}); \ No newline at end of file