parent
0f4dc4f4b8
commit
477a952023
@ -1 +1,2 @@ |
|||||||
export { default as RemixApp } from './lib/remix-app/remix-app' |
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