Skip to content

Commit 2a68d2a

Browse files
committed
WIP - alternate "VNC settings" editing
In a proper dialog with its own discoverable button.
1 parent 8128512 commit 2a68d2a

File tree

4 files changed

+72
-73
lines changed

4 files changed

+72
-73
lines changed

src/components/vm/consoles/common.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { fmt_to_fragments } from 'utils.jsx';
2626

2727
const _ = cockpit.gettext;
2828

29-
export const RemoteConnectionInfo = ({ url, editor }) => {
29+
export const RemoteConnectionInfo = ({ url }) => {
3030
function TLI(term, description) {
3131
// What is this? Java?
3232
return (
@@ -71,7 +71,6 @@ export const RemoteConnectionInfo = ({ url, editor }) => {
7171
</ClipboardCopy>
7272
</>
7373
}
74-
{ editor }
7574
</TextContent>
7675
);
7776
};

src/components/vm/consoles/consoles.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ export const ConsoleCard = ({ vm, config, type, setType, onAddErrorNotification,
124124
} else if (inactive_vnc) {
125125
body = (
126126
<VncInactive
127-
vm={vm}
128-
inactive_vnc={inactive_vnc}
129-
onAddErrorNotification={onAddErrorNotification} />
127+
vm={vm}
128+
inactive_vnc={inactive_vnc}
129+
connectionAddress={connection_address()}
130+
onAddErrorNotification={onAddErrorNotification} />
130131
);
131132
} else {
132133
body = <SpiceInactive vm={vm} />;

src/components/vm/consoles/spice.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { Button } from "@patternfly/react-core/dist/esm/components/Button";
2424
import { EmptyState, EmptyStateBody, EmptyStateFooter, EmptyStateActions } from "@patternfly/react-core/dist/esm/components/EmptyState";
2525
import { Split, SplitItem } from "@patternfly/react-core/dist/esm/layouts/Split/index.js";
2626
import { Popover } from "@patternfly/react-core/dist/esm/components/Popover";
27+
import { HelpIcon } from "@patternfly/react-icons";
2728

2829
import { useDialogs } from 'dialogs';
2930

@@ -37,6 +38,9 @@ const SpiceFooter = ({ onLaunch, connectionAddress, spice }) => {
3738
<div className="vm-console-footer">
3839
<Split>
3940
<SplitItem isFilled>
41+
<span><b>SPICE</b> {connectionAddress}{ spice ? ":" + spice.port : ""}</span>
42+
</SplitItem>
43+
<SplitItem>
4044
<Button
4145
variant="secondary"
4246
onClick={onLaunch}
@@ -53,7 +57,7 @@ const SpiceFooter = ({ onLaunch, connectionAddress, spice }) => {
5357
cockpit.format("spice://$0:$1", connectionAddress, spice.port)} />}
5458
>
5559
<Button variant="plain">
56-
{_("How to connect")}
60+
<HelpIcon />
5761
</Button>
5862
</Popover>
5963
</SplitItem>

src/components/vm/consoles/vnc.jsx

Lines changed: 62 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { Form, FormGroup, FormHelperText } from "@patternfly/react-core/dist/esm
2828
import { HelperText, HelperTextItem } from "@patternfly/react-core/dist/esm/components/HelperText";
2929
import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput";
3030
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";
3232
import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner";
3333

3434
import {
@@ -42,6 +42,11 @@ import { Split, SplitItem } from "@patternfly/react-core/dist/esm/layouts/Split/
4242
import { KebabDropdown } from 'cockpit-components-dropdown.jsx';
4343
import { DropdownItem } from "@patternfly/react-core/dist/esm/components/Dropdown";
4444

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+
4550
import { logDebug } from '../../../helpers.js';
4651
import { RemoteConnectionInfo } from './common';
4752
import { domainSendKey, domainAttachVnc, domainChangeVncSettings, domainGet } from '../../../libvirtApi/domain.js';
@@ -69,16 +74,19 @@ const Enum = {
6974
KEY_DELETE: 111,
7075
};
7176

72-
const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
77+
const VncEditModal = ({ vm, inactive_vnc }) => {
7378
const config_port = (inactive_vnc.port == -1) ? "" : (inactive_vnc.port || "");
7479
const config_password = inactive_vnc.password || "";
7580

81+
const Dialogs = useDialogs();
7682
const [editing, setEditing] = useState(false);
7783
const [applyInProgress, setApplyInProgress] = useState(false);
7884
const [port, setPort] = useState(config_port);
7985
const [password, setPassword] = useState(config_password);
8086
const [showPassword, setShowPassword] = useState(false);
8187
const [portError, setPortError] = useState(null);
88+
const [applyError, setApplyError] = useState(null);
89+
const [applyErrorDetails, setApplyErrorDetails] = useState(null);
8290

8391
async function apply() {
8492
if (port != "" && (!port.match("^[0-9]+$") || Number(port) < 5900)) {
@@ -98,49 +106,45 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
98106
try {
99107
await domainChangeVncSettings(vm, vncParams);
100108
domainGet({ connectionName: vm.connectionName, id: vm.id });
101-
setEditing(false);
109+
Dialogs.close();
102110
} 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);
108113
}
109114

110115
setApplyInProgress(false);
111116
}
112117

113-
function cancel() {
114-
setEditing(false);
115-
setPortError(null);
116-
setPort(config_port);
117-
setPassword(config_password);
118-
}
119-
120118
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+
}
138143
<Form onSubmit={e => e.preventDefault()} isHorizontal>
139144
<FormGroup label={_("Port")}>
140145
<TextInput
141146
type="text"
142-
value={editing ? port : config_port}
143-
isDisabled={!editing}
147+
value={port}
144148
onChange={(_ev, val) => { setPortError(null); setPort(val) }} />
145149
<FormHelperText>
146150
<HelperText>
@@ -157,8 +161,7 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
157161
<InputGroup>
158162
<TextInput
159163
type={showPassword ? "text" : "password"}
160-
value={editing ? password : config_password}
161-
isDisabled={!editing}
164+
value={password}
162165
onChange={(_ev, val) => setPassword(val)} />
163166
<Button
164167
variant="control"
@@ -168,30 +171,13 @@ const VncSettings = ({ vm, inactive_vnc, onAddErrorNotification }) => {
168171
</InputGroup>
169172
</FormGroup>
170173
</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>
191175
);
192176
};
193177

194178
const VncFooter = ({ vm, vnc, inactive_vnc, connected, connectionAddress, onLaunch, onDisconnect, onAddErrorNotification }) => {
179+
const Dialogs = useDialogs();
180+
195181
const renderDropdownItem = keyName => {
196182
return (
197183
<DropdownItem
@@ -238,6 +224,15 @@ const VncFooter = ({ vm, vnc, inactive_vnc, connected, connectionAddress, onLaun
238224
<div className="vm-console-footer">
239225
<Split>
240226
<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>
241236
<Button
242237
variant="secondary"
243238
onClick={onLaunch}
@@ -249,19 +244,15 @@ const VncFooter = ({ vm, vnc, inactive_vnc, connected, connectionAddress, onLaun
249244
className="ct-remote-viewer-popover"
250245
headerContent={_("Remote viewer")}
251246
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+
}
258251
>
259252
<Button variant="plain">
260-
{_("How to connect")}
253+
<HelpIcon />
261254
</Button>
262255
</Popover>
263-
</SplitItem>
264-
<SplitItem>
265256
<KebabDropdown
266257
toggleButtonId="vnc-actions"
267258
position='right'
@@ -409,15 +400,19 @@ export class VncActive extends React.Component {
409400
}
410401
}
411402

412-
export const VncInactive = ({ vm, inactive_vnc, onAddErrorNotification }) => {
403+
export const VncInactive = ({ vm, inactive_vnc, connectionAddress, onAddErrorNotification }) => {
413404
return (
414405
<>
415406
<EmptyState>
416407
<EmptyStateBody>
417408
{_("Start the virtual machine to access the console")}
418409
</EmptyStateBody>
419410
</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} />
421416
</>
422417
);
423418
};

0 commit comments

Comments
 (0)