interface IPluginApi { React: typeof React; GQL: any; Event: { addEventListener: (event: string, callback: (e: CustomEvent) => void) => void; }; libraries: { ReactRouterDOM: { Link: React.FC; Route: React.FC; NavLink: React.FC; }, Bootstrap: { Button: React.FC; Nav: React.FC & { Link: React.FC; Item: React.FC; }; Tab: React.FC & { Pane: React.FC; } }, FontAwesomeSolid: { faEthernet: any; }, Intl: { FormattedMessage: React.FC; } }, loadableComponents: any; components: Record>; utils: { NavUtils: any; loadComponents: any; }, hooks: any; patch: { before: (target: string, fn: Function) => void; instead: (target: string, fn: Function) => void; after: (target: string, fn: Function) => void; }, register: { route: (path: string, component: React.FC) => void; } } (function () { const PluginApi = (window as any).PluginApi as IPluginApi; const React = PluginApi.React; const GQL = PluginApi.GQL; const { Button, Nav, Tab } = PluginApi.libraries.Bootstrap; const { faEthernet } = PluginApi.libraries.FontAwesomeSolid; const { Link, NavLink, } = PluginApi.libraries.ReactRouterDOM; const { NavUtils } = PluginApi.utils; PluginApi.Event.addEventListener("stash:location", (e) => console.log("Page Changed", e.detail.data.location.pathname, e.detail.data.location.search)) const ScenePerformer: React.FC<{ performer: any; }> = ({ performer }) => { // PluginApi.components may not be registered when the outside function is run // need to initialise these inside the function component const { HoverPopover, } = PluginApi.components; const popoverContent = React.useMemo( () => (
{performer.name
), [performer] ); return ( {performer.name} ); }; function SceneDetails(props: any) { const { TagLink, } = PluginApi.components; function maybeRenderPerformers() { if (props.scene.performers.length <= 0) return; return (
{props.scene.performers.map((performer: any) => ( ))}
); } function maybeRenderTags() { if (props.scene.tags.length <= 0) return; return (
{props.scene.tags.map((tag: any) => ( ))}
); } return (
{props.scene.date} {maybeRenderPerformers()} {maybeRenderTags()}
); } function Overlays() { return Custom overlay; } PluginApi.patch.instead("SceneCard.Details", function (props: any, _: any, original: any) { return ; }); PluginApi.patch.instead("SceneCard.Overlays", function (props: any, _: any, original: (props: any) => any) { return <>{original({...props})}; }); PluginApi.patch.instead("FrontPage", function (props: any, _: any, original: (props: any) => any) { return <>

Hello from Test React!

{original({...props})}; }); const TestPage: React.FC = () => { const componentsToLoad = [ PluginApi.loadableComponents.SceneCard, PluginApi.loadableComponents.PerformerSelect, ]; const componentsLoading = PluginApi.hooks.useLoadComponents(componentsToLoad); const { SceneCard, LoadingIndicator, PerformerSelect, } = PluginApi.components; // read a random scene and show a scene card for it const { data } = GQL.useFindScenesQuery({ variables: { filter: { per_page: 1, sort: "random", }, }, }); const scene = data?.findScenes.scenes[0]; if (componentsLoading) return ( ); return (
This is a test page.
{!!scene && }
{}} values={[]} />
); }; PluginApi.register.route("/plugins/test-react", TestPage); PluginApi.patch.before("SettingsToolsSection", function (props: any) { const { Setting, } = PluginApi.components; return [ { children: ( <> {props.children} } /> ), }, ]; }); PluginApi.patch.before("MainNavBar.UtilityItems", function (props: any) { const { Icon, } = PluginApi.components; return [ { children: ( <> {props.children} ) } ] }); PluginApi.patch.before("ScenePage.Tabs", function (props: any) { return [ { children: ( <> {props.children} Test React tab ), }, ]; }); PluginApi.patch.before("ScenePage.TabContent", function (props: any) { return [ { children: ( <> {props.children} Test React tab content {props.scene.id} ), }, ]; }); })();