[ABANDONED] React/Redux front end for the Flexor social network.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

128 lines
4.5 KiB

import React, { FC, useEffect, useRef } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import classNames from 'classnames'
import { getOrigin } from 'src/utils'
import { useConfig, useDeepCompareEffect } from 'src/hooks'
import { fetchInstallations, setSelectedInstallation, setHeight as setComposerHeight, setError as setComposerError } from 'src/actions/composer'
import { getInstallations, getSelectedInstallation, getError, getHeight as getComposerHeight } from 'src/selectors/composer'
import { AppState, Installation, ClassDictionary } from 'src/types'
import { IncomingMessageData, OutgoingMessageData } from 'src/types/communicator'
const Composer: FC = () => {
const installations = useSelector<AppState, Installation[]>(getInstallations)
const installation = useSelector<AppState, Installation | undefined>(getSelectedInstallation)
const height = useSelector<AppState, number>(getComposerHeight)
const error = useSelector<AppState, string | undefined>(getError)
const config = useConfig()
const dispatch = useDispatch()
const ref = useRef<HTMLIFrameElement>(null)
const composerUrl = installation ? installation.app.composerUrl : undefined
const showComposer = !!composerUrl && !error
const composerClasses: ClassDictionary = {
composer: true,
'composer-empty': !showComposer,
}
useEffect(() => {
dispatch(fetchInstallations())
}, [])
useDeepCompareEffect(() => {
if (!composerUrl) return
if (!installation) return
if (error) return
const listener = async (event: MessageEvent) => {
const origin = getOrigin(composerUrl)
if (event.origin !== origin) return
const postMessage = (message: OutgoingMessageData) => {
if (ref.current && ref.current.contentWindow) {
ref.current.contentWindow.postMessage(JSON.stringify(message), origin)
}
}
let data: IncomingMessageData | undefined
try {
data = JSON.parse(event.data)
} catch (err) {
dispatch(setComposerError('Invalid payload'))
return
}
if (!data) return
if (data.publicKey !== installation.app.publicKey) {
const message = 'Invalid publicKey'
dispatch(setComposerError(message))
postMessage({
name: data.name,
error: message,
})
}
switch (data.name) {
case 'init':
postMessage({
name: data.name,
settings: installation.settings,
})
break
case 'setHeight':
const { height = 100 } = data.content
dispatch(setComposerHeight(Math.max(Math.min(height, 400), 100)))
postMessage({
name: data.name,
})
break
}
}
window.addEventListener('message', listener, false)
return () => {
window.removeEventListener('message', listener, false)
}
}, [installation, error])
const handleClick = (id: string) => {
if (installation && installation.id === id) {
dispatch(setSelectedInstallation())
return
}
dispatch(setSelectedInstallation(id))
}
return (
<div className="container composer-container">
<div className={classNames(composerClasses)}>
{showComposer &&
<iframe ref={ref} src={composerUrl} scrolling="no" style={{ height, width: '100%', overflow: 'hidden' }} />
}
{error && <span className="has-text-danger">Composer Error: {error}</span>}
{(!showComposer && !error) && <span>Choose an App.</span>}
</div>
<div className="installations is-flex">
{installations.map(i => (
<div key={i.id} className={installation && installation.id === i.id ? 'selected' : ''} onClick={() => handleClick(i.id)}>
<img src={`${config.blobUrl}${i.app.iconImageUrl}`} alt={i.app.name} style={{ width: 32 }} />
<p className="is-size-7 has-text-weight-bold">{i.app.name}</p>
</div>
))}
</div>
</div>
)
}
export default Composer