Path: blob/trunk/javascript/grid-ui/src/components/Node/Node.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 { Box, Card, CardContent, Dialog, DialogActions, DialogContent, DialogTitle, Grid, IconButton, Typography, Button, keyframes, styled } from '@mui/material'18import React, { useState, useRef } from 'react'19import { Videocam as VideocamIcon } from '@mui/icons-material'20import NodeDetailsDialog from './NodeDetailsDialog'21import NodeLoad from './NodeLoad'22import Stereotypes from './Stereotypes'23import OsLogo from '../common/OsLogo'24import LiveView from '../LiveView/LiveView'2526const pulse = keyframes`270% {28box-shadow: 0 0 0 0 rgba(25, 118, 210, 0.7);29transform: scale(1);30}3150% {32box-shadow: 0 0 0 5px rgba(25, 118, 210, 0);33transform: scale(1.05);34}35100% {36box-shadow: 0 0 0 0 rgba(25, 118, 210, 0);37transform: scale(1);38}39`4041const LiveIconButton = styled(IconButton)(({ theme }) => ({42marginLeft: theme.spacing(1),43position: 'relative',44'&::after': {45content: '""',46position: 'absolute',47width: '100%',48height: '100%',49borderRadius: '50%',50animation: `${pulse} 2s infinite`,51zIndex: 052}53}))5455function getVncUrl(session, origin) {56try {57const parsed = JSON.parse(session.capabilities)58let vnc = parsed['se:vnc'] ?? ''59if (vnc.length > 0) {60try {61const url = new URL(origin)62const vncUrl = new URL(vnc)63url.pathname = vncUrl.pathname64url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'65return url.href66} catch (error) {67console.log(error)68return ''69}70}71return ''72} catch (e) {73return ''74}75}7677function Node (props) {78const { node, sessions = [], origin } = props79const [liveViewSessionId, setLiveViewSessionId] = useState('')80const liveViewRef = useRef<{ disconnect: () => void }>(null)8182const vncSession = sessions.find(session => {83try {84const capabilities = JSON.parse(session.capabilities)85return capabilities['se:vnc'] !== undefined && capabilities['se:vnc'] !== ''86} catch (e) {87return false88}89})9091const handleLiveViewIconClick = () => {92if (vncSession) {93setLiveViewSessionId(vncSession.id)94}95}9697const handleDialogClose = () => {98if (liveViewRef.current) {99liveViewRef.current.disconnect()100}101setLiveViewSessionId('')102}103const getCardStyle = (status: string) => ({104height: '100%',105flexGrow: 1,106opacity: status === 'DOWN' ? 0.25 : 1,107bgcolor: (status === 'DOWN' || status === 'DRAINING') ? 'grey.A100' : ''108})109110return (111<>112<Card sx={getCardStyle(node.status)}>113<CardContent sx={{ pl: 2, pr: 1 }}>114<Grid115container116justifyContent="space-between"117spacing={1}118>119<Grid item xs={10}>120<Typography121color="textPrimary"122gutterBottom123variant="h6"124>125<Box fontWeight="fontWeightBold" mr={1} display="inline">126URI:127</Box>128{node.uri}129</Typography>130</Grid>131<Grid item xs={2}>132<Typography133color="textPrimary"134gutterBottom135variant="h6"136>137<OsLogo osName={node.osInfo.name}/>138<NodeDetailsDialog node={node}/>139</Typography>140</Grid>141<Grid item xs={12}>142<Box display="flex" alignItems="center">143<Stereotypes stereotypes={node.slotStereotypes}/>144{vncSession && (145<LiveIconButton onClick={handleLiveViewIconClick} size='medium' color="primary">146<VideocamIcon data-testid="VideocamIcon" />147</LiveIconButton>148)}149</Box>150</Grid>151<Grid item xs={12}>152<NodeLoad node={node}/>153</Grid>154</Grid>155</CardContent>156</Card>157{vncSession && liveViewSessionId && (158<Dialog159onClose={handleDialogClose}160aria-labelledby='live-view-dialog'161open={liveViewSessionId === vncSession.id}162fullWidth163maxWidth='xl'164fullScreen165>166<DialogTitle id='live-view-dialog'>167<Typography gutterBottom component='span' sx={{ paddingX: '10px' }}>168<Box fontWeight='fontWeightBold' mr={1} display='inline'>169Node Session Live View170</Box>171{node.uri}172</Typography>173</DialogTitle>174<DialogContent dividers sx={{ height: '600px' }}>175<LiveView176ref={liveViewRef as any}177url={getVncUrl(vncSession, origin)}178scaleViewport179onClose={handleDialogClose}180/>181</DialogContent>182<DialogActions>183<Button onClick={handleDialogClose} color='primary' variant='contained'>184Close185</Button>186</DialogActions>187</Dialog>188)}189</>190)191}192193export default Node194195196