diff --git a/src/app.js b/src/app.js index e1960e56..ae9de431 100644 --- a/src/app.js +++ b/src/app.js @@ -125,7 +125,7 @@ class DPanelApp extends LitElement { this.router.addAfterHook(() => store.updateState({ appContext: { menuVisible: false }})) // Clear some contexts on route change - this.router.addBeforeHook(() => store.clearContext(['pupContext'])); + this.router.addBeforeHook(() => store.clearContext(['pupContext, guiContext'])); if (isUnauthedRoute()) { setTimeout(() => { this.ready = true; }, 1500) diff --git a/src/pages/page-iframe/index.js b/src/pages/page-iframe/index.js index a1549fd6..c2a176e5 100644 --- a/src/pages/page-iframe/index.js +++ b/src/pages/page-iframe/index.js @@ -27,15 +27,6 @@ class IframeView extends LitElement { } `; - // Whenever pupContext source is available, set ready to true. - updated(changedProperties) { - super.updated(changedProperties); - if (changedProperties.has('pupContext')) { - this.ready = !!this.context.store.pupContext.manifest.gui.source; - this.requestUpdate(); - } - } - constructor() { super(); @@ -43,7 +34,7 @@ class IframeView extends LitElement { this.context = new StoreSubscriber(this, store) // Ready state is dependent on the pupContext having an iframe source. - this.ready = !!this.context.store.pupContext.manifest.gui.source + this.ready = !!this.context?.store?.guiContext?.ready; this.iframeElement = null; this.debouncedHandleResize = debounce(this.handleResize, 300); @@ -59,7 +50,7 @@ class IframeView extends LitElement { connectedCallback() { super.connectedCallback(); this.updateComplete.then(() => { - if (!this.ready) return + if (!this.context?.store?.guiContext?.ready) return this.iframeElement = this.shadowRoot.querySelector('iframe'); @@ -114,8 +105,8 @@ class IframeView extends LitElement { } render() { - const { pupContext } = this.context.store - if (!this.ready) { + const { guiContext } = this.context.store; + if (!guiContext.ready) { return html`
@@ -125,11 +116,15 @@ class IframeView extends LitElement { ` } - if (this.ready) { + if (guiContext.ready) { + const iframeURL = `${window.location.protocol}//${window.location.hostname}:${guiContext.port}` return html` -
- +
`; } diff --git a/src/pages/page-pup-library-listing/renders/actions.js b/src/pages/page-pup-library-listing/renders/actions.js index 59836339..bab94b2b 100644 --- a/src/pages/page-pup-library-listing/renders/actions.js +++ b/src/pages/page-pup-library-listing/renders/actions.js @@ -73,10 +73,8 @@ export function renderActions(labels, hasLogs) { size="large" variant="warning" ?disabled="${statusId !== 'running'}" - @click=${() => { - window.open(`${window.location.protocol}//${window.location.hostname}:${entry.port}/`, "_blank"); - }} - "> + href="/pups/${pkg.state.id}/ui/${encodeURIComponent(entry.name)}" + > Launch ${entry.name} diff --git a/src/router/config.js b/src/router/config.js index 20541dc0..1c8e67cb 100644 --- a/src/router/config.js +++ b/src/router/config.js @@ -1,6 +1,7 @@ import { isAuthed, loadPup, + setActiveGui, asPage, withDialog, performLogout, @@ -43,6 +44,16 @@ export const routes = [ pageTitle: "Settings", before: [isAuthed, asPage, withDialog], }, + { + path: "/pups/:pupid/ui/:guiname", + component: "x-page-pup-iframe", + dynamicTitle: true, + before: [isAuthed, asPage], + pageAction: "back", + animate: true, + before: [loadPup, asPage], + after: [setActiveGui], + }, { path: "/pups", component: "x-page-pup-library", diff --git a/src/router/middleware.js b/src/router/middleware.js index fd9204c7..a031d2ba 100644 --- a/src/router/middleware.js +++ b/src/router/middleware.js @@ -61,6 +61,11 @@ export async function loadPup(context, commands) { store.updateState({ pupContext: { ...pup, ready: true, result: 200 }, }); + + // Attach pup to route context in case subsequent route middleware + // wish to refer to it. + context.pup = pup + } catch (error) { console.warn("[500] loadPup middleware: failure.", error); store.updateState({ @@ -69,6 +74,39 @@ export async function loadPup(context, commands) { } } +export async function setActiveGui(context, commands) { + const pupId = context.params.pupid; + const guiName = decodeURIComponent(context.params.guiname); + + try { + // Attempt to get the pup from memory + let pup = pkgController.getPupMaster({ pupId, lookupType: 'byStatePupId' }).pup; + + // Find the pup gui details + const guis = pup.state.webUIs || []; + const gui = guis.find((g) => g.name === guiName) + + if (!gui) { + throw new Error(`Cannot find gui by name: ${guiName}`) + } + + store.updateState({ + guiContext: { + ready: true, + result: 200, + id: guiName, + ...gui + }, + }); + + } catch (err) { + console.log('woof', err); + store.updateState({ + pupContext: { ready: true, result: 500 }, + }); + } +} + export function isAuthed(context, commands) { if (store.networkContext.token) { return undefined; @@ -84,7 +122,7 @@ export function performLogout(context, commands) { export function asPage(context, commands) { if (context.route.dynamicTitle) { - context.route.pageTitle = decodeURIComponent(context.params.pupname); + context.route.pageTitle = decodeURIComponent(context.params.pupname || context.params.guiname); context.route.pageAction = "back"; } diff --git a/src/state/store.js b/src/state/store.js index e51011be..c7e38fcb 100644 --- a/src/state/store.js +++ b/src/state/store.js @@ -43,6 +43,10 @@ class Store { ready: false, result: null, }; + this.guiContext = { + id: null, + ready: false, + }; this.promptContext = { display: false, name: "transaction", @@ -161,6 +165,9 @@ class Store { if (partialState.pupContext) { this.pupContext = { ...this.pupContext, ...partialState.pupContext }; } + if (partialState.guiContext) { + this.guiContext = { ...this.guiContext, ...partialState.guiContext }; + } if (partialState.promptContext) { this.promptContext = { ...this.promptContext,