diff --git a/src/def.d.ts b/src/def.d.ts index 6b1bd0e..802715a 100644 --- a/src/def.d.ts +++ b/src/def.d.ts @@ -76,11 +76,14 @@ export enum ButtonColors { interface ConfirmationAlertOptions { title?: string; - content: string | JSX.Element | JSX.Element[]; + content: string | JSX.Element | (string | JSX.Element)[]; confirmText?: string; confirmColor?: ButtonColors; onConfirm: () => void; + secondaryConfirmText?: string; + onConfirmSecondary?: () => void; cancelText?: string; + isDismissable?: boolean; } interface InputAlertProps { diff --git a/src/ui/alerts.ts b/src/ui/alerts.ts index be6cc4a..efb5a6f 100644 --- a/src/ui/alerts.ts +++ b/src/ui/alerts.ts @@ -5,21 +5,18 @@ import InputAlert from "@ui/components/InputAlert"; const Alerts = findByProps("openLazy", "close"); interface InternalConfirmationAlertOptions extends Omit { - content: string | JSX.Element | JSX.Element[] | undefined; - body: string | undefined; - children: JSX.Element | JSX.Element[]; + content?: ConfirmationAlertOptions["content"]; + body?: ConfirmationAlertOptions["content"]; }; export function showConfirmationAlert(options: ConfirmationAlertOptions) { const internalOptions = options as InternalConfirmationAlertOptions; - if (typeof options.content === "string") { - internalOptions.body = options.content; - } else { - internalOptions.children = options.content; - }; - + internalOptions.body = options.content; delete internalOptions.content; + + internalOptions.isDismissable ??= true; + return Alerts.show(internalOptions); }; @@ -27,4 +24,4 @@ export const showCustomAlert = (component: React.ComponentType, props: any) => A importer: async () => () => React.createElement(component, props), }); -export const showInputAlert = (options: InputAlertProps) => showCustomAlert(InputAlert as React.ComponentType, options); \ No newline at end of file +export const showInputAlert = (options: InputAlertProps) => showCustomAlert(InputAlert as React.ComponentType, options); diff --git a/src/ui/quickInstall/forumPost.tsx b/src/ui/quickInstall/forumPost.tsx index 468321b..772042f 100644 --- a/src/ui/quickInstall/forumPost.tsx +++ b/src/ui/quickInstall/forumPost.tsx @@ -1,5 +1,5 @@ -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 { findByName, findByProps } from "@metro/filters"; import { after } from "@lib/patcher"; import { installPlugin } from "@lib/plugins"; import { installTheme } from "@lib/themes"; @@ -20,7 +20,7 @@ export default () => after("default", ForumPostLongPressActionSheet, ([{ thread if (thread.guild_id !== DISCORD_SERVER_ID) return; // Determine what type of addon this is. - let postType: string; + let postType: "Plugin" | "Theme"; if (thread.parent_id === PLUGINS_CHANNEL_ID) { postType = "Plugin"; } else if (thread.parent_id === THEMES_CHANNEL_ID && window.__vendetta_loader?.features.themes) { @@ -62,4 +62,4 @@ export default () => after("default", ForumPostLongPressActionSheet, ([{ thread } /> ); -}); \ No newline at end of file +}); diff --git a/src/ui/quickInstall/index.ts b/src/ui/quickInstall/index.ts index f3dd1bd..38e5f77 100644 --- a/src/ui/quickInstall/index.ts +++ b/src/ui/quickInstall/index.ts @@ -8,4 +8,4 @@ export default function initQuickInstall() { 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 deleted file mode 100644 index 6294e80..0000000 --- a/src/ui/quickInstall/url.ts +++ /dev/null @@ -1,31 +0,0 @@ -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, (args) => { - if (args[0].key !== "LongPressUrl") return; - - const { header: { title: url }, options } = args[0]; - - let urlType: string; - if (url.startsWith(PROXY_PREFIX)) { - urlType = "Plugin"; - } else if (url.endsWith(".json") && window.__vendetta_loader?.features.themes) { - 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 diff --git a/src/ui/quickInstall/url.tsx b/src/ui/quickInstall/url.tsx new file mode 100644 index 0000000..a9ae6d2 --- /dev/null +++ b/src/ui/quickInstall/url.tsx @@ -0,0 +1,73 @@ +import { PROXY_PREFIX, THEMES_CHANNEL_ID } from "@lib/constants"; +import { findByProps } from "@metro/filters"; +import { ReactNative as RN, channels, url } from "@metro/common"; +import { after, instead } from "@lib/patcher"; +import { installPlugin } from "@lib/plugins"; +import { installTheme } from "@lib/themes"; +import { getAssetIDByName } from "@ui/assets"; +import { showToast } from "@ui/toasts"; +import { showConfirmationAlert } from "../alerts"; + +const showSimpleActionSheet = findByProps("showSimpleActionSheet"); +const handleClick = findByProps("handleClick"); +const { openURL } = url; +const { getChannelId } = channels; +const { getChannel } = findByProps("getChannel"); + +const { TextStyleSheet } = findByProps("TextStyleSheet"); + +function typeFromUrl(url: string) { + if (url.startsWith(PROXY_PREFIX)) { + return "Plugin"; + } else if (url.endsWith(".json") && window.__vendetta_loader?.features.themes) { + return "Theme"; + } else return; +} + +function installWithToast(type: "Plugin" | "Theme", url: string) { + (type === "Plugin" ? installPlugin : installTheme)(url).then(() => { + showToast("Successfully installed", getAssetIDByName("Check")); + }).catch((e: Error) => { + showToast(e.message, getAssetIDByName("Small")); + }); +} + +export default () => { + const patches = new Array; + + patches.push(after("showSimpleActionSheet", showSimpleActionSheet, (args) => { + if (args[0].key !== "LongPressUrl") return; + const { header: { title: url }, options } = args[0]; + + const urlType = typeFromUrl(url); + if (!urlType) return; + + options.push({ + label: `Install ${urlType}`, + onPress: () => installWithToast(urlType, url), + }); + })); + + patches.push(instead("handleClick", handleClick, async function (this: any, args, orig) { + const { href: url } = args[0]; + + const urlType = typeFromUrl(url); + if (!urlType) return orig.apply(this, args); + + // Make clicking on theme links only work in #themes, should there be a theme proxy in the future, this can be removed. + if (urlType === "Theme" && getChannel(getChannelId())?.parent_id !== THEMES_CHANNEL_ID) + return orig.apply(this, args); + + showConfirmationAlert({ + title: "Hold Up", + content: [`This link is a `, {urlType}, `, would you like to install it?`], + onConfirm: () => installWithToast(urlType, url), + confirmText: "Install", + cancelText: "Cancel", + secondaryConfirmText: "Open in Browser", + onConfirmSecondary: () => openURL(url), + }); + })); + + return () => patches.forEach((p) => p()); +};