parent
0f4dc4f4b8
commit
477a952023
@ -1 +1,2 @@ |
||||
export { default as RemixApp } from './lib/remix-app/remix-app' |
||||
export { default as IframeReactPlugin } from './lib/remix-app/plugins/IFrameReactPlugin' |
||||
|
@ -0,0 +1,29 @@ |
||||
import React, { useRef, useState } from 'react' |
||||
import IframeReactPlugin from '../../plugins/IFrameReactPlugin' |
||||
|
||||
interface IFramePluginViewProps { |
||||
plugin: IframeReactPlugin |
||||
} |
||||
|
||||
const IFramePluginView = (props: IFramePluginViewProps) => { |
||||
const ref = useRef() |
||||
const [loading, isLoading] = useState<boolean>(true) |
||||
|
||||
const loaded = () => { |
||||
props.plugin.shake(ref.current) |
||||
isLoading(false) |
||||
} |
||||
|
||||
const loader = <div className='d-flex justify-content-center align-items-center'> |
||||
<div className='spinner-border' role="status"> |
||||
<span className='sr-only'>Loading...</span> |
||||
</div> |
||||
</div> |
||||
|
||||
return (<> |
||||
<div className={loading ? '' : 'd-none'}>{loader}</div> |
||||
<iframe onLoad={loaded} ref={ref} src={props.plugin.profile.url} title={props.plugin.name} id={props.plugin.name} seamless={true} sandbox='allow-popups allow-scripts allow-same-origin allow-forms allow-top-navigation'></iframe> |
||||
</>) |
||||
} |
||||
|
||||
export default IFramePluginView |
@ -0,0 +1,85 @@ |
||||
import type { Message, Profile, ExternalProfile, LocationProfile } from '@remixproject/plugin-utils' |
||||
import { PluginConnector } from '@remixproject/engine' |
||||
import React from 'react' // eslint-disable-line
|
||||
import IFramePluginView from '../components/panels/iFramePluginView' |
||||
|
||||
export type IframeProfile = Profile & LocationProfile & ExternalProfile |
||||
|
||||
/** |
||||
* Connect an Iframe client to the engine. |
||||
* @dev This implements the ViewPlugin as it cannot extends two class. Maybe use a mixin at some point |
||||
*/ |
||||
class IframeReactPlugin extends PluginConnector { |
||||
// Listener is needed to remove the listener
|
||||
private readonly listener = ['message', (e: MessageEvent) => this.getEvent(e), false] as const |
||||
private container: any |
||||
private origin: string |
||||
private source: Window |
||||
private url: string |
||||
|
||||
constructor (public profile: IframeProfile) { |
||||
super(profile) |
||||
} |
||||
|
||||
/** Implement "activate" of the ViewPlugin */ |
||||
connect (url: string) { |
||||
this.profile.url = url |
||||
this.render() |
||||
} |
||||
|
||||
addToView () { |
||||
this.call(this.profile.location, 'addView', this.profile, this.container) |
||||
} |
||||
|
||||
shake (iframe: any) { |
||||
return new Promise((resolve, reject) => { |
||||
// Wait for the iframe to load and handshake
|
||||
|
||||
if (!iframe.contentWindow) { |
||||
reject(new Error(`${this.name} plugin cannot find url ${this.profile.url}`)) |
||||
} |
||||
this.origin = new URL(iframe.src).origin |
||||
this.source = iframe.contentWindow |
||||
window.addEventListener(...this.listener) |
||||
this.handshake() |
||||
.then(resolve) |
||||
.catch(reject) |
||||
//
|
||||
}) |
||||
} |
||||
|
||||
/** Implement "deactivate" of the ViewPlugin */ |
||||
disconnect () { |
||||
console.trace('disconnect') |
||||
window.removeEventListener(...this.listener) |
||||
return this.call(this.profile.location, 'removeView', this.profile) |
||||
.catch(console.error) |
||||
} |
||||
|
||||
/** Get message from the iframe */ |
||||
private async getEvent (event: MessageEvent) { |
||||
if (event.source !== this.source) return // Filter only messages that comes from this iframe
|
||||
if (event.origin !== this.origin) return // Filter only messages that comes from this origin
|
||||
const message: Message = event.data |
||||
this.getMessage(message) |
||||
} |
||||
|
||||
/** |
||||
* Post a message to the iframe of this plugin |
||||
* @param message The message to post |
||||
*/ |
||||
protected send (message: Partial<Message>) { |
||||
if (!this.source) { |
||||
throw new Error('No window attached to Iframe yet') |
||||
} |
||||
this.source.postMessage(message, this.origin) |
||||
} |
||||
|
||||
/** Create and return the iframe */ |
||||
render () { |
||||
this.container = <IFramePluginView plugin={this} /> |
||||
this.addToView() |
||||
} |
||||
} |
||||
|
||||
export default IframeReactPlugin |
Loading…
Reference in new issue