Path: blob/trunk/javascript/grid-ui/src/components/LiveView/LiveView.tsx
3987 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react'18import RFB from '@novnc/novnc/lib/rfb'19import PasswordDialog from './PasswordDialog'20import MuiAlert, { AlertProps } from '@mui/material/Alert'21import Snackbar from '@mui/material/Snackbar'22import { useTheme } from '@mui/material/styles'2324const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert (25props,26ref27) {28return <MuiAlert elevation={6} ref={ref} variant='filled' {...props} />29})3031const LiveView = forwardRef((props, ref) => {32let canvas: any = null33const theme = useTheme()3435const [open, setOpen] = useState(false)36const [message, setMessage] = useState('')37const [rfb, setRfb] = useState<RFB>(null)38const [openErrorAlert, setOpenErrorAlert] = useState(false)39const [openSuccessAlert, setOpenSuccessAlert] = useState(false)4041const handlePasswordDialog = (state: boolean): void => {42setOpen(state)43}4445const disconnect = () => {46if (!rfb) {47return48}49rfb.disconnect()50setRfb(null)51}5253useImperativeHandle(ref, () => ({54disconnect55}))5657const connect = () => {58disconnect()5960if (!canvas) {61return62}6364const newRfb = new RFB.default(canvas, props.url, {})65newRfb.scaleViewport = props.scaleViewport66newRfb.background = theme.palette.background.default67newRfb.addEventListener('credentialsrequired', handleCredentials)68newRfb.addEventListener('securityfailure', securityFailed)69newRfb.addEventListener('connect', connectedToServer)70newRfb.addEventListener('disconnect', disconnect)71setRfb(newRfb)72}7374const registerChild = ref => {75canvas = ref76}7778useEffect(() => {79connect()80return () => {81disconnect()82}83// eslint-disable-next-line84}, [])8586useEffect(() => {87if (rfb) {88rfb.scaleViewport = props.scaleViewport89}90})9192const securityFailed = (event: any) => {93let errorMessage94if ('reason' in event.detail) {95errorMessage =96'Connection has been rejected with reason: ' + event.detail.reason97} else {98errorMessage = 'New connection has been rejected'99}100setMessage(errorMessage)101setOpenErrorAlert(true)102}103104const handleCredentials = () => {105handlePasswordDialog(true)106}107108const connectedToServer = () => {109setOpenSuccessAlert(true)110}111112const handleCredentialsEntered = (password: string) => {113rfb.sendCredentials({ username: '', password: password })114setOpen(false)115}116117const handlePasswordDialogClose = () => {118disconnect()119props.onClose()120}121122const handleMouseEnter = () => {123if (!rfb) {124return125}126rfb.focus()127}128129const handleMouseLeave = () => {130if (!rfb) {131return132}133rfb.blur()134}135136const handleClose = (event?: React.SyntheticEvent | Event,137reason?: string) => {138if (reason === 'clickaway') {139return140}141setOpenErrorAlert(false)142disconnect()143props.onClose()144}145146return (147<div148style={149{150width: '100%',151height: '100%'152}153}154ref={registerChild}155onMouseEnter={handleMouseEnter}156onMouseLeave={handleMouseLeave}157>158<PasswordDialog159title='LiveView (VNC) Password'160open={open}161openDialog={handlePasswordDialog}162onConfirm={handleCredentialsEntered}163onCancel={handlePasswordDialogClose}164/>165<Snackbar166open={openErrorAlert}167anchorOrigin={{ vertical: 'top', horizontal: 'center' }}168autoHideDuration={6000}169onClose={handleClose}170>171<Alert severity='error' sx={{ width: '100%' }}>172{message}173</Alert>174</Snackbar>175<Snackbar176open={openSuccessAlert}177anchorOrigin={{ vertical: 'top', horizontal: 'center' }}178autoHideDuration={4000}179onClose={() => setOpenSuccessAlert(false)}180>181<Alert severity='success' sx={{ width: '100%' }}>182Connected successfully!183</Alert>184</Snackbar>185</div>186)187})188189export default LiveView190191192