mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
* refactored `Interactive` class to allow more HapticDevice devices * simplified api hooks * update creation of `interactive` to pass `stashConfig` * updated UIPluginApi to mention `PluginApi.InteractiveUtils`
197 lines
7.2 KiB
TypeScript
197 lines
7.2 KiB
TypeScript
import React from "react";
|
|
import ReactDOM from "react-dom";
|
|
import * as ReactRouterDOM from "react-router-dom";
|
|
import Mousetrap from "mousetrap";
|
|
import MousetrapPause from "mousetrap-pause";
|
|
import NavUtils from "./utils/navigation";
|
|
import * as GQL from "src/core/generated-graphql";
|
|
import * as StashService from "src/core/StashService";
|
|
import * as Apollo from "@apollo/client";
|
|
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";
|
|
import Event from "./hooks/event";
|
|
import { after, before, components, instead, RegisterComponent } from "./patch";
|
|
import { useSettings } from "./components/Settings/context";
|
|
import { useInteractive } from "./hooks/Interactive/context";
|
|
import InteractiveUtils from "./hooks/Interactive/utils";
|
|
import { useLightbox, useGalleryLightbox } from "./hooks/Lightbox/hooks";
|
|
|
|
// due to code splitting, some components may not have been loaded when a plugin
|
|
// page is loaded. This function will load all components passed to it.
|
|
// The components need to be imported here. Any required imports will be added
|
|
// to the loadableComponents object in the plugin api.
|
|
async function loadComponents(c: (() => Promise<unknown>)[]) {
|
|
await Promise.all(c.map((fn) => fn()));
|
|
}
|
|
|
|
// useLoadComponents is a hook that loads all components passed to it.
|
|
// It returns a boolean indicating whether the components are still loading.
|
|
function useLoadComponents(toLoad: (() => Promise<unknown>)[]) {
|
|
const [loading, setLoading] = React.useState(true);
|
|
const [componentList] = React.useState(toLoad);
|
|
|
|
async function load(c: (() => Promise<unknown>)[]) {
|
|
await loadComponents(c);
|
|
setLoading(false);
|
|
}
|
|
|
|
React.useEffect(() => {
|
|
setLoading(true);
|
|
load(componentList);
|
|
}, [componentList]);
|
|
|
|
return loading;
|
|
}
|
|
|
|
function registerRoute(path: string, component: React.FC) {
|
|
before("PluginRoutes", function (props: React.PropsWithChildren<{}>) {
|
|
return [
|
|
{
|
|
children: (
|
|
<>
|
|
{props.children}
|
|
<ReactRouterDOM.Route path={path} component={component} />
|
|
</>
|
|
),
|
|
},
|
|
];
|
|
});
|
|
}
|
|
|
|
export const PluginApi = {
|
|
React,
|
|
ReactDOM,
|
|
GQL,
|
|
libraries: {
|
|
ReactRouterDOM,
|
|
Bootstrap,
|
|
Apollo,
|
|
Intl,
|
|
FontAwesomeRegular,
|
|
FontAwesomeSolid,
|
|
FontAwesomeBrands,
|
|
Mousetrap,
|
|
MousetrapPause,
|
|
ReactSelect,
|
|
},
|
|
register: {
|
|
// register a route to be added to the main router
|
|
route: registerRoute,
|
|
// register a component to be added to the components object
|
|
component: RegisterComponent,
|
|
},
|
|
loadableComponents: {
|
|
// add any lazy loaded imports here - this is coarse-grained and will load all components
|
|
// in the import
|
|
Performers: () => import("./components/Performers/Performers"),
|
|
FrontPage: () => import("./components/FrontPage/FrontPage"),
|
|
Scenes: () => import("./components/Scenes/Scenes"),
|
|
Settings: () => import("./components/Settings/Settings"),
|
|
Stats: () => import("./components/Stats"),
|
|
Studios: () => import("./components/Studios/Studios"),
|
|
Galleries: () => import("./components/Galleries/Galleries"),
|
|
Groups: () => import("./components/Groups/Groups"),
|
|
Tags: () => import("./components/Tags/Tags"),
|
|
Images: () => import("./components/Images/Images"),
|
|
|
|
SubmitStashBoxDraft: () => import("src/components/Dialogs/SubmitDraft"),
|
|
GenerateDialog: () => import("./components/Dialogs/GenerateDialog"),
|
|
|
|
ScenePlayer: () => import("src/components/ScenePlayer/ScenePlayer"),
|
|
|
|
GalleryViewer: () => import("src/components/Galleries/GalleryViewer"),
|
|
|
|
DeleteScenesDialog: () => import("./components/Scenes/DeleteScenesDialog"),
|
|
SceneList: () => import("./components/Scenes/SceneList"),
|
|
SceneMarkerList: () => import("./components/Scenes/SceneMarkerList"),
|
|
Scene: () => import("./components/Scenes/SceneDetails/Scene"),
|
|
SceneCreate: () => import("./components/Scenes/SceneDetails/SceneCreate"),
|
|
|
|
ExternalPlayerButton: () =>
|
|
import("./components/Scenes/SceneDetails/ExternalPlayerButton"),
|
|
QueueViewer: () => import("./components/Scenes/SceneDetails/QueueViewer"),
|
|
SceneMarkersPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneMarkersPanel"),
|
|
SceneFileInfoPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneFileInfoPanel"),
|
|
SceneDetailPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneDetailPanel"),
|
|
SceneHistoryPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneHistoryPanel"),
|
|
SceneGroupPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneGroupPanel"),
|
|
SceneGalleriesPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneGalleriesPanel"),
|
|
SceneVideoFilterPanel: () =>
|
|
import("./components/Scenes/SceneDetails/SceneVideoFilterPanel"),
|
|
SceneScrapeDialog: () =>
|
|
import("./components/Scenes/SceneDetails/SceneScrapeDialog"),
|
|
SceneQueryModal: () =>
|
|
import("./components/Scenes/SceneDetails/SceneQueryModal"),
|
|
|
|
LightboxComponent: () => import("src/hooks/Lightbox/Lightbox"),
|
|
|
|
// intentionally omitting these for now
|
|
// Setup: () => import("./components/Setup/Setup"),
|
|
// Migrate: () => import("./components/Setup/Migrate"),
|
|
// SceneFilenameParser: () => import("./components/SceneFilenameParser/SceneFilenameParser"),
|
|
// SceneDuplicateChecker: () => import("./components/SceneDuplicateChecker/SceneDuplicateChecker"),
|
|
// Manual: () => import("./Manual"),
|
|
|
|
// individual components here
|
|
// add components as needed for plugins that provide pages
|
|
SceneCard: () => import("./components/Scenes/SceneCard"),
|
|
PerformerSelect: () => import("./components/Performers/PerformerSelect"),
|
|
TagLink: () => import("./components/Shared/TagLink"),
|
|
PerformerCard: () => import("./components/Performers/PerformerCard"),
|
|
},
|
|
components,
|
|
utils: {
|
|
InteractiveUtils,
|
|
NavUtils,
|
|
StashService,
|
|
loadComponents,
|
|
},
|
|
hooks: {
|
|
useLoadComponents,
|
|
useSpriteInfo,
|
|
useToast,
|
|
useSettings,
|
|
useInteractive,
|
|
useLightbox,
|
|
useGalleryLightbox,
|
|
},
|
|
patch: {
|
|
// intercept the arguments of supported functions
|
|
// the provided function should accept the arguments and return the new arguments
|
|
before,
|
|
// replace a function with a new one implementation
|
|
// the provided function will be called with the arguments passed to the original function
|
|
// and the original function as the last argument
|
|
// only one instead function can be registered per component
|
|
instead,
|
|
// intercept the result of supported functions
|
|
// the provided function will be called with the arguments passed to the original function
|
|
// and the result of the original function
|
|
after,
|
|
},
|
|
Event: Event,
|
|
};
|
|
|
|
export default PluginApi;
|
|
|
|
interface IWindow {
|
|
PluginApi: typeof PluginApi;
|
|
}
|
|
|
|
const localWindow = window as unknown as IWindow;
|
|
|
|
// export the plugin api to the window object
|
|
localWindow.PluginApi = PluginApi;
|