From 4d61c886615d2a209fb50fc26d2f75e89b7a2cdb Mon Sep 17 00:00:00 2001 From: CJ <72030708+cj12312021@users.noreply.github.com> Date: Sun, 16 Mar 2025 19:20:39 -0500 Subject: [PATCH] Patchable ExternalLinkButtons component (#5727) * Patchable ExternalLinkButtons component * added fontAwesomeBrands * use ExternalLinkButtons on groups page --- .../components/Groups/GroupDetails/Group.tsx | 4 +- .../components/Shared/ExternalLinksButton.tsx | 125 +++++++++--------- ui/v2.5/src/docs/en/Manual/UIPluginApi.md | 3 + ui/v2.5/src/pluginApi.d.ts | 3 + ui/v2.5/src/pluginApi.tsx | 2 + 5 files changed, 74 insertions(+), 63 deletions(-) diff --git a/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx b/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx index 913b2bc52..4636d1e5e 100644 --- a/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx +++ b/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx @@ -28,7 +28,7 @@ import { DetailImage } from "src/components/Shared/DetailImage"; import { useRatingKeybinds } from "src/hooks/keybinds"; import { useLoadStickyHeader } from "src/hooks/detailsPanel"; import { useScrollToTopOnMount } from "src/hooks/scrollToTop"; -import { ExternalLinksButton } from "src/components/Shared/ExternalLinksButton"; +import { ExternalLinkButtons } from "src/components/Shared/ExternalLinksButton"; import { BackgroundImage } from "src/components/Shared/DetailsPage/BackgroundImage"; import { DetailTitle } from "src/components/Shared/DetailsPage/DetailTitle"; import { ExpandCollapseButton } from "src/components/Shared/CollapseButton"; @@ -374,7 +374,7 @@ const GroupPage: React.FC = ({ group, tabKey }) => { /> )} - + diff --git a/ui/v2.5/src/components/Shared/ExternalLinksButton.tsx b/ui/v2.5/src/components/Shared/ExternalLinksButton.tsx index 7ecea9b30..7d8072d07 100644 --- a/ui/v2.5/src/components/Shared/ExternalLinksButton.tsx +++ b/ui/v2.5/src/components/Shared/ExternalLinksButton.tsx @@ -6,73 +6,76 @@ import { IconDefinition, faLink } from "@fortawesome/free-solid-svg-icons"; import { useMemo } from "react"; import { faInstagram, faTwitter } from "@fortawesome/free-brands-svg-icons"; import ReactDOM from "react-dom"; +import { PatchComponent } from "src/patch"; export const ExternalLinksButton: React.FC<{ icon?: IconDefinition; urls: string[]; className?: string; -}> = ({ urls, icon = faLink, className = "" }) => { - if (!urls.length) { - return null; - } - - const Menu = () => - ReactDOM.createPortal( - - {urls.map((url) => ( - - {url} - - ))} - , - document.body - ); - - return ( - - - - - - - ); -}; - -export const ExternalLinkButtons: React.FC<{ urls: string[] | undefined }> = ({ - urls, -}) => { - const urlSpecs = useMemo(() => { - if (!urls?.length) { - return []; +}> = PatchComponent( + "ExternalLinksButton", + ({ urls, icon = faLink, className = "" }) => { + if (!urls.length) { + return null; } - const twitter = urls.filter((u) => - u.match(/https?:\/\/(?:www\.)?(?:twitter|x).com\//) - ); - const instagram = urls.filter((u) => - u.match(/https?:\/\/(?:www\.)?instagram.com\//) - ); - const others = urls.filter( - (u) => !twitter.includes(u) && !instagram.includes(u) - ); + const Menu = () => + ReactDOM.createPortal( + + {urls.map((url) => ( + + {url} + + ))} + , + document.body + ); - return [ - { icon: faLink, className: "", urls: others }, - { icon: faTwitter, className: "twitter", urls: twitter }, - { icon: faInstagram, className: "instagram", urls: instagram }, - ]; - }, [urls]); + return ( + + + + + + + ); + } +); - return ( - <> - {urlSpecs.map((spec, i) => ( - - ))} - - ); -}; +export const ExternalLinkButtons: React.FC<{ urls: string[] | undefined }> = + PatchComponent("ExternalLinkButtons", ({ urls }) => { + const urlSpecs = useMemo(() => { + if (!urls?.length) { + return []; + } + + const twitter = urls.filter((u) => + u.match(/https?:\/\/(?:www\.)?(?:twitter|x).com\//) + ); + const instagram = urls.filter((u) => + u.match(/https?:\/\/(?:www\.)?instagram.com\//) + ); + const others = urls.filter( + (u) => !twitter.includes(u) && !instagram.includes(u) + ); + + return [ + { icon: faLink, className: "", urls: others }, + { icon: faTwitter, className: "twitter", urls: twitter }, + { icon: faInstagram, className: "instagram", urls: instagram }, + ]; + }, [urls]); + + return ( + <> + {urlSpecs.map((spec, i) => ( + + ))} + + ); + }); diff --git a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md index f76b9e939..ada9b9131 100644 --- a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md +++ b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md @@ -29,6 +29,7 @@ This namespace contains the generated graphql client interface. This is a low-le - `Intl` - `FontAwesomeRegular` - `FontAwesomeSolid` +- `FontAwesomeBrands` - `Mousetrap` - `MousetrapPause` - `ReactSelect` @@ -147,6 +148,8 @@ Returns `void`. - `ConstantSetting` - `CountrySelect` - `DateInput` +- `ExternalLinkButtons` +- `ExternalLinksButton` - `FolderSelect` - `FrontPage` - `GalleryIDSelect` diff --git a/ui/v2.5/src/pluginApi.d.ts b/ui/v2.5/src/pluginApi.d.ts index d1c79b3bb..cb2c05c02 100644 --- a/ui/v2.5/src/pluginApi.d.ts +++ b/ui/v2.5/src/pluginApi.d.ts @@ -614,6 +614,7 @@ declare namespace PluginApi { const Bootstrap: typeof import("react-bootstrap"); const FontAwesomeRegular: typeof import("@fortawesome/free-regular-svg-icons"); const FontAwesomeSolid: typeof import("@fortawesome/free-solid-svg-icons"); + const FontAwesomeBrands: typeof import("@fortawesome/free-brands-svg-icons"); const Intl: typeof import("react-intl"); const Mousetrap: typeof import("mousetrap"); const ReactSelect: typeof import("react-select"); @@ -697,6 +698,8 @@ declare namespace PluginApi { PerformerImagesPanel: React.FC; TabTitleCounter: React.FC; PerformerCard: React.FC; + ExternalLinkButtons: React.FC; + ExternalLinksButton: React.FC; "PerformerCard.Popovers": React.FC; "PerformerCard.Details": React.FC; "PerformerCard.Overlays": React.FC; diff --git a/ui/v2.5/src/pluginApi.tsx b/ui/v2.5/src/pluginApi.tsx index f94502fc4..88b3272e0 100644 --- a/ui/v2.5/src/pluginApi.tsx +++ b/ui/v2.5/src/pluginApi.tsx @@ -11,6 +11,7 @@ import * as Bootstrap from "react-bootstrap"; import * as Intl from "react-intl"; import * as FontAwesomeSolid from "@fortawesome/free-solid-svg-icons"; import * as FontAwesomeRegular from "@fortawesome/free-regular-svg-icons"; +import * as FontAwesomeBrands from "@fortawesome/free-brands-svg-icons"; import * as ReactSelect from "react-select"; import { useSpriteInfo } from "./hooks/sprite"; import { useToast } from "./hooks/Toast"; @@ -72,6 +73,7 @@ export const PluginApi = { Intl, FontAwesomeRegular, FontAwesomeSolid, + FontAwesomeBrands, Mousetrap, MousetrapPause, ReactSelect,