Path: blob/trunk/javascript/grid-ui/src/components/LiveView/LiveView.tsx
2887 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'2223const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert (24props,25ref26) {27return <MuiAlert elevation={6} ref={ref} variant='filled' {...props} />28})2930const LiveView = forwardRef((props, ref) => {31let canvas: any = null3233const [open, setOpen] = useState(false)34const [message, setMessage] = useState('')35const [rfb, setRfb] = useState<RFB>(null)36const [openErrorAlert, setOpenErrorAlert] = useState(false)37const [openSuccessAlert, setOpenSuccessAlert] = useState(false)3839const handlePasswordDialog = (state: boolean): void => {40setOpen(state)41}4243const disconnect = () => {44if (!rfb) {45return46}47rfb.disconnect()48setRfb(null)49}5051useImperativeHandle(ref, () => ({52disconnect53}))5455const connect = () => {56disconnect()5758if (!canvas) {59return60}6162const newRfb = new RFB.default(canvas, props.url, {})63newRfb.scaleViewport = props.scaleViewport64newRfb.background = 'rgb(247,248,248)'65newRfb.addEventListener('credentialsrequired', handleCredentials)66newRfb.addEventListener('securityfailure', securityFailed)67newRfb.addEventListener('connect', connectedToServer)68newRfb.addEventListener('disconnect', disconnect)69setRfb(newRfb)70}7172const registerChild = ref => {73canvas = ref74}7576useEffect(() => {77connect()78return () => {79disconnect()80}81// eslint-disable-next-line82}, [])8384useEffect(() => {85if (rfb) {86rfb.scaleViewport = props.scaleViewport87}88})8990const securityFailed = (event: any) => {91let errorMessage92if ('reason' in event.detail) {93errorMessage =94'Connection has been rejected with reason: ' + event.detail.reason95} else {96errorMessage = 'New connection has been rejected'97}98setMessage(errorMessage)99setOpenErrorAlert(true)100}101102const handleCredentials = () => {103handlePasswordDialog(true)104}105106const connectedToServer = () => {107setOpenSuccessAlert(true)108}109110const handleCredentialsEntered = (password: string) => {111rfb.sendCredentials({ username: '', password: password })112setOpen(false)113}114115const handlePasswordDialogClose = () => {116disconnect()117props.onClose()118}119120const handleMouseEnter = () => {121if (!rfb) {122return123}124rfb.focus()125}126127const handleMouseLeave = () => {128if (!rfb) {129return130}131rfb.blur()132}133134const handleClose = (event?: React.SyntheticEvent | Event,135reason?: string) => {136if (reason === 'clickaway') {137return138}139setOpenErrorAlert(false)140disconnect()141props.onClose()142}143144return (145<div146style={147{148width: '100%',149height: '100%'150}151}152ref={registerChild}153onMouseEnter={handleMouseEnter}154onMouseLeave={handleMouseLeave}155>156<PasswordDialog157title='LiveView (VNC) Password'158open={open}159openDialog={handlePasswordDialog}160onConfirm={handleCredentialsEntered}161onCancel={handlePasswordDialogClose}162/>163<Snackbar164open={openErrorAlert}165anchorOrigin={{ vertical: 'top', horizontal: 'center' }}166autoHideDuration={6000}167onClose={handleClose}168>169<Alert severity='error' sx={{ width: '100%' }}>170{message}171</Alert>172</Snackbar>173<Snackbar174open={openSuccessAlert}175anchorOrigin={{ vertical: 'top', horizontal: 'center' }}176autoHideDuration={4000}177onClose={() => setOpenSuccessAlert(false)}178>179<Alert severity='success' sx={{ width: '100%' }}>180Connected successfully!181</Alert>182</Snackbar>183</div>184)185})186187export default LiveView188189190