From: Dominik Csapak <d.csapak@proxmox.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>,
Folke Gleumes <f.gleumes@proxmox.com>
Subject: Re: [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu
Date: Wed, 17 Apr 2024 10:33:05 +0200 [thread overview]
Message-ID: <4cd12eef-54b0-4b09-8d21-e6d05cff7d3c@proxmox.com> (raw)
In-Reply-To: <044ddf67407c9e2ef69e527b4d28c6685be96f6b.camel@proxmox.com>
On 4/17/24 10:19, Folke Gleumes wrote:
> On Wed, 2024-04-17 at 08:45 +0200, Dominik Csapak wrote:
>> similar to how it works for qemu, but add a confirmation dialog
>> so one does not accidentally shutdown or reboot a node.
>>
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>> changes from v1:
>> * add an AlertDialog as confirmation before executing the action
>>
>> lib/bloc/pve_node_overview_bloc.dart | 11 +++
>> lib/widgets/pve_node_overview.dart | 24 ++++++
>> .../pve_node_power_settings_widget.dart | 84
>> +++++++++++++++++++
>> 3 files changed, 119 insertions(+)
>> create mode 100644 lib/widgets/pve_node_power_settings_widget.dart
>>
>> diff --git a/lib/bloc/pve_node_overview_bloc.dart
>> b/lib/bloc/pve_node_overview_bloc.dart
>> index d14ff79..19d6563 100644
>> --- a/lib/bloc/pve_node_overview_bloc.dart
>> +++ b/lib/bloc/pve_node_overview_bloc.dart
>> @@ -57,9 +57,20 @@ class PveNodeOverviewBloc
>> final disks = await apiClient.getNodeDisksList(nodeID);
>> yield latestState.rebuild((b) => b..disks.replace(disks));
>> }
>> + if (event is PerformNodeAction) {
>> + await apiClient.doResourceAction(nodeID, '', 'node',
>> event.action,
>> + parameters: <String, String>{});
>> + yield latestState;
>> + }
>> }
>> }
>>
>> abstract class PveNodeOverviewEvent {}
>>
>> class UpdateNodeStatus extends PveNodeOverviewEvent {}
>> +
>> +class PerformNodeAction extends PveNodeOverviewEvent {
>> + final PveClusterResourceAction action;
>> +
>> + PerformNodeAction(this.action);
>> +}
>> diff --git a/lib/widgets/pve_node_overview.dart
>> b/lib/widgets/pve_node_overview.dart
>> index 7b65c0e..ad9a3b2 100644
>> --- a/lib/widgets/pve_node_overview.dart
>> +++ b/lib/widgets/pve_node_overview.dart
>> @@ -8,6 +8,7 @@ import
>> 'package:pve_flutter_frontend/states/pve_node_overview_state.dart';
>> import
>> 'package:pve_flutter_frontend/states/pve_task_log_state.dart';
>> import 'package:pve_flutter_frontend/utils/renderers.dart';
>> import 'package:pve_flutter_frontend/utils/utils.dart';
>> +import
>> 'package:pve_flutter_frontend/widgets/pve_node_power_settings_widget.
>> dart';
>> import
>> 'package:pve_flutter_frontend/widgets/proxmox_capacity_indicator.dart
>> ';
>> import
>> 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.d
>> art';
>> import
>> 'package:pve_flutter_frontend/widgets/pve_action_card_widget.dart';
>> @@ -189,6 +190,16 @@ class PveNodeOverview extends StatelessWidget {
>> child: Row(
>> mainAxisAlignment:
>> MainAxisAlignment.spaceEvenly,
>> children: <Widget>[
>> + ActionCard(
>> + icon: const Icon(
>> + Icons.power_settings_new,
>> + size: 55,
>> + color: Colors.white24,
>> + ),
>> + title: 'Power Settings',
>> + onTap: () =>
>> + showPowerMenuBottomSheet(context,
>> nBloc),
>> + ),
>> ActionCard(
>> icon: const Icon(
>> Icons.queue_play_next,
>> @@ -443,4 +454,17 @@ class PveNodeOverview extends StatelessWidget {
>> },
>> );
>> }
>> +
>> + Future<T?> showPowerMenuBottomSheet<T>(
>> + BuildContext context, PveNodeOverviewBloc nodeBloc) async {
>> + return showModalBottomSheet(
>> + shape: const RoundedRectangleBorder(
>> + borderRadius: BorderRadius.vertical(top:
>> Radius.circular(10))),
>> + context: context,
>> + builder: (context) => Provider.value(
>> + value: nodeBloc,
>> + child: const PveNodePowerSettings(),
>> + ),
>> + );
>> + }
>> }
>> diff --git a/lib/widgets/pve_node_power_settings_widget.dart
>> b/lib/widgets/pve_node_power_settings_widget.dart
>> new file mode 100644
>> index 0000000..621ac68
>> --- /dev/null
>> +++ b/lib/widgets/pve_node_power_settings_widget.dart
>> @@ -0,0 +1,84 @@
>> +import 'package:flutter/material.dart';
>> +import 'package:provider/provider.dart';
>> +import
>> 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
>> +import
>> 'package:pve_flutter_frontend/bloc/pve_node_overview_bloc.dart';
>> +import
>> 'package:pve_flutter_frontend/states/pve_node_overview_state.dart';
>> +import
>> 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.d
>> art';
>> +
>> +class PveNodePowerSettings extends StatelessWidget {
>> + const PveNodePowerSettings({
>> + super.key,
>> + });
>> + @override
>> + Widget build(BuildContext context) {
>> + final bloc = Provider.of<PveNodeOverviewBloc>(context);
>> + return ProxmoxStreamBuilder<PveNodeOverviewBloc,
>> PveNodeOverviewState>(
>> + bloc: bloc,
>> + builder: (context, state) {
>> + return SafeArea(
>> + child: SingleChildScrollView(
>> + child: Container(
>> + constraints: BoxConstraints(
>> + minHeight: MediaQuery.of(context).size.height /
>> 3),
>> + child: Column(
>> + mainAxisSize: MainAxisSize.min,
>> + children: <Widget>[
>> + ListTile(
>> + leading: const Icon(Icons.autorenew),
>> + title: const Text(
>> + "Reboot",
>> + style: TextStyle(fontWeight:
>> FontWeight.bold),
>> + ),
>> + subtitle: const Text("Reboot Node"),
>> + onTap: () => action(context,
>> + PveClusterResourceAction.reboot, bloc,
>> "reboot"),
>> + ),
>> + ListTile(
>> + leading: const Icon(Icons.power_settings_new),
>> + title: const Text(
>> + "Shutdown",
>> + style: TextStyle(fontWeight:
>> FontWeight.bold),
>> + ),
>> + subtitle: const Text("Shutdown Node"),
>> + onTap: () => action(context,
>> + PveClusterResourceAction.shutdown, bloc,
>> "shutdown"),
>> + ),
>> + ],
>> + ),
>> + ),
>> + ),
>> + );
>> + });
>> + }
>> +
>> + void action(BuildContext context, PveClusterResourceAction action,
>> + PveNodeOverviewBloc bloc, String actionText) async {
>> + if (await showDialog(
>> + context: context,
>> + builder: (context) {
>> + return AlertDialog(
>> + contentPadding: const EdgeInsets.fromLTRB(24.0, 12.0,
>> 24.0, 16.0),
>> + title: const Row(
>> + mainAxisAlignment: MainAxisAlignment.spaceBetween,
>> + children: <Widget>[
>> + Text('Confirm'),
>> + Icon(Icons.warning),
>> + ],
>> + ),
>> + content: Text(
>> + "Are you sure you want to $actionText node
>> '${bloc.nodeID}'?"),
>> + actions: [
>> + TextButton(
>> + onPressed: () => Navigator.of(context).pop(true),
>> + child: const Text("Yes")),
>> + TextButton(
>> + onPressed: () => Navigator.of(context).pop(false),
>> + child: const Text("No"))
>> + ],
>
> The order of buttons probably should be reversed. According to material
> guidelines a dismissive action has to be placed to the left of
> confirming actions [0]. In practice, I accidentally shut down my test
> VM because I intuitively went for the leftmost button to abort. To make
> the actions even more obvious, they could be phrased as Cancel and
> Shutdown/Reboot.
fair enough, i used the same order as we do for the desktop ui
(left yes, right no), but that is probably also not super ideal?
i'll send a v3
>
>> + );
>> + })) {
>> + bloc.events.add(PerformNodeAction(action));
>> + if (context.mounted) Navigator.of(context).pop();
>> + }
>> + }
>> +}
>
> Besides the button ordering, everything worked well, so if those are
> changed, consider this:
>
> Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
>
> [0] https://m2.material.io/components/dialogs#actions
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2024-04-17 8:33 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-17 6:45 Dominik Csapak
2024-04-17 8:19 ` Folke Gleumes
2024-04-17 8:33 ` Dominik Csapak [this message]
2024-04-17 8:48 ` [pve-devel] applied: " Thomas Lamprecht
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4cd12eef-54b0-4b09-8d21-e6d05cff7d3c@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=f.gleumes@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.