@@ -28,7 +28,7 @@ import { Form, FormGroup, FormHelperText } from "@patternfly/react-core/dist/esm
28
28
import { HelperText , HelperTextItem } from "@patternfly/react-core/dist/esm/components/HelperText" ;
29
29
import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput" ;
30
30
import { InputGroup } from "@patternfly/react-core/dist/esm/components/InputGroup" ;
31
- import { PencilAltIcon , EyeIcon , EyeSlashIcon , CheckIcon , TimesIcon , PendingIcon } from "@patternfly/react-icons" ;
31
+ import { PencilAltIcon , EyeIcon , EyeSlashIcon , CheckIcon , TimesIcon , PendingIcon , HelpIcon } from "@patternfly/react-icons" ;
32
32
import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner" ;
33
33
34
34
import {
@@ -42,6 +42,11 @@ import { Split, SplitItem } from "@patternfly/react-core/dist/esm/layouts/Split/
42
42
import { KebabDropdown } from 'cockpit-components-dropdown.jsx' ;
43
43
import { DropdownItem } from "@patternfly/react-core/dist/esm/components/Dropdown" ;
44
44
45
+ import { Modal , ModalVariant } from "@patternfly/react-core/dist/esm/components/Modal" ;
46
+ import { ModalError } from 'cockpit-components-inline-notification.jsx' ;
47
+ import { NeedsShutdownAlert } from '../../common/needsShutdown.jsx' ;
48
+ import { useDialogs } from 'dialogs' ;
49
+
45
50
import { logDebug } from '../../../helpers.js' ;
46
51
import { RemoteConnectionInfo } from './common' ;
47
52
import { domainSendKey , domainAttachVnc , domainChangeVncSettings , domainGet } from '../../../libvirtApi/domain.js' ;
@@ -69,16 +74,19 @@ const Enum = {
69
74
KEY_DELETE : 111 ,
70
75
} ;
71
76
72
- const VncSettings = ( { vm, inactive_vnc, onAddErrorNotification } ) => {
77
+ const VncEditModal = ( { vm, inactive_vnc } ) => {
73
78
const config_port = ( inactive_vnc . port == - 1 ) ? "" : ( inactive_vnc . port || "" ) ;
74
79
const config_password = inactive_vnc . password || "" ;
75
80
81
+ const Dialogs = useDialogs ( ) ;
76
82
const [ editing , setEditing ] = useState ( false ) ;
77
83
const [ applyInProgress , setApplyInProgress ] = useState ( false ) ;
78
84
const [ port , setPort ] = useState ( config_port ) ;
79
85
const [ password , setPassword ] = useState ( config_password ) ;
80
86
const [ showPassword , setShowPassword ] = useState ( false ) ;
81
87
const [ portError , setPortError ] = useState ( null ) ;
88
+ const [ applyError , setApplyError ] = useState ( null ) ;
89
+ const [ applyErrorDetails , setApplyErrorDetails ] = useState ( null ) ;
82
90
83
91
async function apply ( ) {
84
92
if ( port != "" && ( ! port . match ( "^[0-9]+$" ) || Number ( port ) < 5900 ) ) {
@@ -98,49 +106,45 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
98
106
try {
99
107
await domainChangeVncSettings ( vm , vncParams ) ;
100
108
domainGet ( { connectionName : vm . connectionName , id : vm . id } ) ;
101
- setEditing ( false ) ;
109
+ Dialogs . close ( ) ;
102
110
} catch ( ex ) {
103
- onAddErrorNotification ( {
104
- text : cockpit . format ( _ ( "VNC settings of $0 could not be saved" ) , vm . name ) ,
105
- detail : ex . message ,
106
- resourceId : vm . id ,
107
- } ) ;
111
+ setApplyError ( _ ( "VNC settings could not be saved" ) ) ;
112
+ setApplyErrorDetail ( ex . message ) ;
108
113
}
109
114
110
115
setApplyInProgress ( false ) ;
111
116
}
112
117
113
- function cancel ( ) {
114
- setEditing ( false ) ;
115
- setPortError ( null ) ;
116
- setPort ( config_port ) ;
117
- setPassword ( config_password ) ;
118
- }
119
-
120
118
return (
121
- < >
122
- < Text component = { TextVariants . h6 } >
123
- { _ ( "Configuration" ) }
124
- { ! editing
125
- ? < Button variant = "plain" onClick = { ( ) => setEditing ( true ) } >
126
- < PencilAltIcon />
127
- </ Button >
128
- : < >
129
- < Button variant = "plain" onClick = { apply } isDisabled = { applyInProgress } >
130
- { applyInProgress ? < Spinner size = "md" /> : < CheckIcon /> }
131
- </ Button >
132
- < Button variant = "plain" onClick = { cancel } isDisabled = { applyInProgress } >
133
- < TimesIcon />
134
- </ Button >
135
- </ >
136
- }
137
- </ Text >
119
+ < Modal
120
+ id = "vnc-edit-dialog"
121
+ position = "top"
122
+ variant = { ModalVariant . medium }
123
+ title = { _ ( "Edit VNC settings" ) }
124
+ isOpen
125
+ onClose = { Dialogs . close }
126
+ footer = {
127
+ < >
128
+ < Button isDisabled = { ! ! portError } id = "vnc-edit-save" variant = 'primary' onClick = { apply } >
129
+ { _ ( "Save" ) }
130
+ </ Button >
131
+ < Button id = "vnc-edit-cancel" variant = 'link' onClick = { Dialogs . close } >
132
+ { _ ( "Cancel" ) }
133
+ </ Button >
134
+ </ >
135
+ }
136
+ >
137
+ { vm . state === 'running' && ! applyError &&
138
+ < NeedsShutdownAlert idPrefix = "vnc-edit" />
139
+ }
140
+ { applyError &&
141
+ < ModalError dialogError = { applyError } dialogErrorDetail = { applyErrorDetail } />
142
+ }
138
143
< Form onSubmit = { e => e . preventDefault ( ) } isHorizontal >
139
144
< FormGroup label = { _ ( "Port" ) } >
140
145
< TextInput
141
146
type = "text"
142
- value = { editing ? port : config_port }
143
- isDisabled = { ! editing }
147
+ value = { port }
144
148
onChange = { ( _ev , val ) => { setPortError ( null ) ; setPort ( val ) } } />
145
149
< FormHelperText >
146
150
< HelperText >
@@ -157,8 +161,7 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
157
161
< InputGroup >
158
162
< TextInput
159
163
type = { showPassword ? "text" : "password" }
160
- value = { editing ? password : config_password }
161
- isDisabled = { ! editing }
164
+ value = { password }
162
165
onChange = { ( _ev , val ) => setPassword ( val ) } />
163
166
< Button
164
167
variant = "control"
@@ -168,30 +171,13 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
168
171
</ InputGroup >
169
172
</ FormGroup >
170
173
</ Form >
171
- < br />
172
- { vm . state != "shut off" &&
173
- < Text component = { TextVariants . p } >
174
- { _ ( "Changes to the configuration only take affect after a fresh boot." ) }
175
- </ Text >
176
- }
177
- </ >
178
- ) ;
179
- } ;
180
-
181
- const VncConnectionInfo = ( { vm, connection_address, vnc, inactive_vnc, onAddErrorNotification } ) => {
182
- return (
183
- < RemoteConnectionInfo
184
- url = { vnc && cockpit . format ( "vnc://$0:$1" , connection_address , vnc . port ) }
185
- editor = {
186
- inactive_vnc &&
187
- < VncSettings
188
- vm = { vm }
189
- inactive_vnc = { inactive_vnc }
190
- onAddErrorNotification = { onAddErrorNotification } /> } />
174
+ </ Modal >
191
175
) ;
192
176
} ;
193
177
194
178
const VncFooter = ( { vm, vnc, inactive_vnc, connected, connectionAddress, onLaunch, onDisconnect, onAddErrorNotification } ) => {
179
+ const Dialogs = useDialogs ( ) ;
180
+
195
181
const renderDropdownItem = keyName => {
196
182
return (
197
183
< DropdownItem
@@ -238,6 +224,15 @@ const VncFooter = ({ vm, vnc, inactive_vnc, connected, connectionAddress, onLaun
238
224
< div className = "vm-console-footer" >
239
225
< Split >
240
226
< SplitItem isFilled >
227
+ < span > < b > VNC</ b > { connectionAddress } { vnc ? ":" + vnc . port : "" } </ span >
228
+ < Button
229
+ variant = "link"
230
+ onClick = { ( ) => Dialogs . show ( < VncEditModal vm = { vm } inactive_vnc = { inactive_vnc } /> ) }
231
+ >
232
+ { _ ( "edit" ) }
233
+ </ Button >
234
+ </ SplitItem >
235
+ < SplitItem >
241
236
< Button
242
237
variant = "secondary"
243
238
onClick = { onLaunch }
@@ -249,19 +244,15 @@ const VncFooter = ({ vm, vnc, inactive_vnc, connected, connectionAddress, onLaun
249
244
className = "ct-remote-viewer-popover"
250
245
headerContent = { _ ( "Remote viewer" ) }
251
246
hasAutoWidth
252
- bodyContent = { < VncConnectionInfo
253
- vm = { vm }
254
- vnc = { vnc }
255
- inactive_vnc = { inactive_vnc }
256
- connection_address = { connectionAddress }
257
- onAddErrorNotification = { onAddErrorNotification } /> }
247
+ bodyContent = {
248
+ < RemoteConnectionInfo
249
+ url = { vnc && cockpit . format ( "vnc://$0:$1" , connectionAddress , vnc . port ) } />
250
+ }
258
251
>
259
252
< Button variant = "plain" >
260
- { _ ( "How to connect" ) }
253
+ < HelpIcon />
261
254
</ Button >
262
255
</ Popover >
263
- </ SplitItem >
264
- < SplitItem >
265
256
< KebabDropdown
266
257
toggleButtonId = "vnc-actions"
267
258
position = 'right'
@@ -409,15 +400,19 @@ export class VncActive extends React.Component {
409
400
}
410
401
}
411
402
412
- export const VncInactive = ( { vm, inactive_vnc, onAddErrorNotification } ) => {
403
+ export const VncInactive = ( { vm, inactive_vnc, connectionAddress , onAddErrorNotification } ) => {
413
404
return (
414
405
< >
415
406
< EmptyState >
416
407
< EmptyStateBody >
417
408
{ _ ( "Start the virtual machine to access the console" ) }
418
409
</ EmptyStateBody >
419
410
</ EmptyState >
420
- < VncFooter vm = { vm } inactive_vnc = { inactive_vnc } onAddErrorNotification = { onAddErrorNotification } />
411
+ < VncFooter
412
+ vm = { vm }
413
+ inactive_vnc = { inactive_vnc }
414
+ connectionAddress = { connectionAddress }
415
+ onAddErrorNotification = { onAddErrorNotification } />
421
416
</ >
422
417
) ;
423
418
} ;
0 commit comments