blob: ebe5aba157d6085fc6262900baec27dbb9040a8d [file] [log] [blame]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react'
import RFB from '@novnc/novnc/lib/rfb'
import PasswordDialog from './PasswordDialog'
import MuiAlert, { AlertProps } from '@mui/material/Alert'
import Snackbar from '@mui/material/Snackbar'
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert (
props,
ref
) {
return <MuiAlert elevation={6} ref={ref} variant='filled' {...props} />
})
const LiveView = forwardRef((props, ref) => {
let canvas: any = null
const [open, setOpen] = useState(false)
const [message, setMessage] = useState('')
const [rfb, setRfb] = useState<RFB>(null)
const [openErrorAlert, setOpenErrorAlert] = useState(false)
const [openSuccessAlert, setOpenSuccessAlert] = useState(false)
const handlePasswordDialog = (state: boolean): void => {
setOpen(state)
}
const disconnect = () => {
if (!rfb) {
return
}
rfb.disconnect()
setRfb(null)
}
useImperativeHandle(ref, () => ({
disconnect
}))
const connect = () => {
disconnect()
if (!canvas) {
return
}
const newRfb = new RFB.default(canvas, props.url, {})
newRfb.scaleViewport = props.scaleViewport
newRfb.background = 'rgb(247,248,248)'
newRfb.addEventListener('credentialsrequired', handleCredentials)
newRfb.addEventListener('securityfailure', securityFailed)
newRfb.addEventListener('connect', connectedToServer)
newRfb.addEventListener('disconnect', disconnect)
setRfb(newRfb)
}
const registerChild = ref => {
canvas = ref
}
useEffect(() => {
connect()
return () => {
disconnect()
}
// eslint-disable-next-line
}, [])
useEffect(() => {
if (rfb) {
rfb.scaleViewport = props.scaleViewport
}
})
const securityFailed = (event: any) => {
let errorMessage
if ('reason' in event.detail) {
errorMessage =
'Connection has been rejected with reason: ' + event.detail.reason
} else {
errorMessage = 'New connection has been rejected'
}
setMessage(errorMessage)
setOpenErrorAlert(true)
}
const handleCredentials = () => {
handlePasswordDialog(true)
}
const connectedToServer = () => {
setOpenSuccessAlert(true)
}
const handleCredentialsEntered = (password: string) => {
rfb.sendCredentials({ username: '', password: password })
setOpen(false)
}
const handlePasswordDialogClose = () => {
disconnect()
props.onClose()
}
const handleMouseEnter = () => {
if (!rfb) {
return
}
rfb.focus()
}
const handleMouseLeave = () => {
if (!rfb) {
return
}
rfb.blur()
}
const handleClose = (event?: React.SyntheticEvent | Event,
reason?: string) => {
if (reason === 'clickaway') {
return
}
setOpenErrorAlert(false)
disconnect()
props.onClose()
}
return (
<div
style={
{
width: '100%',
height: '100%'
}
}
ref={registerChild}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<PasswordDialog
title='LiveView (VNC) Password'
open={open}
openDialog={handlePasswordDialog}
onConfirm={handleCredentialsEntered}
onCancel={handlePasswordDialogClose}
/>
<Snackbar
open={openErrorAlert}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert severity='error' sx={{ width: '100%' }}>
{message}
</Alert>
</Snackbar>
<Snackbar
open={openSuccessAlert}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
autoHideDuration={4000}
onClose={() => setOpenSuccessAlert(false)}
>
<Alert severity='success' sx={{ width: '100%' }}>
Connected successfully!
</Alert>
</Snackbar>
</div>
)
})
export default LiveView