From db3f04be1b08ba47390472142502f2edd9c2ec24 Mon Sep 17 00:00:00 2001 From: Raghavan Date: Tue, 27 Feb 2024 03:57:28 +0530 Subject: [PATCH] Implement UI event dispatcher/listener (#4492) * page change event * expose event to plugin api * Update UIPluginApi.md * Add to example plugin --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com> --- .../react-component/src/testReact.tsx | 5 +++++ ui/v2.5/src/App.tsx | 6 ++++++ ui/v2.5/src/docs/en/Manual/UIPluginApi.md | 8 ++++++++ ui/v2.5/src/hooks/event.ts | 19 +++++++++++++++++++ ui/v2.5/src/pluginApi.tsx | 2 ++ 5 files changed, 40 insertions(+) create mode 100644 ui/v2.5/src/hooks/event.ts diff --git a/pkg/plugin/examples/react-component/src/testReact.tsx b/pkg/plugin/examples/react-component/src/testReact.tsx index 20760006f34..127920eff9b 100644 --- a/pkg/plugin/examples/react-component/src/testReact.tsx +++ b/pkg/plugin/examples/react-component/src/testReact.tsx @@ -1,6 +1,9 @@ interface IPluginApi { React: typeof React; GQL: any; + Event: { + addEventListener: (event: string, callback: (e: CustomEvent) => void) => void; + }; libraries: { ReactRouterDOM: { Link: React.FC; @@ -53,6 +56,8 @@ interface IPluginApi { 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 }) => { diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index 0d738911ec0..0b63d14ddf5 100644 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -42,6 +42,7 @@ import { lazyComponent } from "./utils/lazyComponent"; import { isPlatformUniquelyRenderedByApple } from "./utils/apple"; import useScript, { useCSS } from "./hooks/useScript"; import { useMemoOnce } from "./hooks/state"; +import Event from "./hooks/event"; import { uniq } from "lodash-es"; import { PluginRoutes } from "./plugins"; @@ -249,6 +250,11 @@ export const App: React.FC = () => { const history = useHistory(); const setupMatch = useRouteMatch(["/setup", "/migrate"]); + // dispatch event when location changes + useEffect(() => { + Event.dispatch("location", "", { location }); + }, [location]); + // redirect to setup or migrate as needed useEffect(() => { if (!systemStatusData) { diff --git a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md index 2a9a54403c1..f58fa0f4785 100644 --- a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md +++ b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md @@ -136,3 +136,11 @@ Registers an after function. An after function is called after the render functi | `fn` | `Function` | The after function. It accepts the same arguments as the original render function, plus the result of the original render function, and is expected to return the rendered component. | Returns `void`. + +#### `PluginApi.Event` + +Allows plugins to listen for Stash's events. + +```js +PluginApi.Event.addEventListener("stash:location", (e) => console.log("Page Changed", e.detail.data.location.pathname)) +``` diff --git a/ui/v2.5/src/hooks/event.ts b/ui/v2.5/src/hooks/event.ts new file mode 100644 index 00000000000..34f93d15a18 --- /dev/null +++ b/ui/v2.5/src/hooks/event.ts @@ -0,0 +1,19 @@ +class StashEvent extends EventTarget { + dispatch(event: string, id?: string, data?: object) { + event = `stash:${event}${id ? `:${id}` : ""}`; + + this.dispatchEvent( + new CustomEvent(event, { + detail: { + event: event, + ...(id ? { id } : {}), + ...(data ? { data } : {}), + }, + }) + ); + } +} + +const Event = new StashEvent(); + +export default Event; diff --git a/ui/v2.5/src/pluginApi.tsx b/ui/v2.5/src/pluginApi.tsx index 0c66f9b096b..f3c2ee7d58b 100644 --- a/ui/v2.5/src/pluginApi.tsx +++ b/ui/v2.5/src/pluginApi.tsx @@ -13,6 +13,7 @@ import * as FontAwesomeSolid from "@fortawesome/free-solid-svg-icons"; import * as FontAwesomeRegular from "@fortawesome/free-regular-svg-icons"; import { useSpriteInfo } from "./hooks/sprite"; import { useToast } from "./hooks/Toast"; +import Event from "./hooks/event"; import { before, instead, after, components, RegisterComponent } from "./patch"; // due to code splitting, some components may not have been loaded when a plugin @@ -106,6 +107,7 @@ export const PluginApi = { // and the result of the original function after, }, + Event: Event, }; export default PluginApi;