From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pve-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 5454C1FF183 for <inbox@lore.proxmox.com>; Wed, 21 May 2025 14:14:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7E01D17C72; Wed, 21 May 2025 14:14:11 +0200 (CEST) Message-ID: <ba75e42d-1b9d-43d7-bbd3-57f6f9b3e122@proxmox.com> Date: Wed, 21 May 2025 14:13:35 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Beta To: pve-devel@lists.proxmox.com References: <20250520080527.37594-1-s.shaji@proxmox.com> <20250520080527.37594-2-s.shaji@proxmox.com> Content-Language: en-US From: Dominik Csapak <d.csapak@proxmox.com> In-Reply-To: <20250520080527.37594-2-s.shaji@proxmox.com> X-SPAM-LEVEL: Spam detection results: 0 AWL -0.029 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: Re: [pve-devel] [PATCH pve_flutter_frontend 1/2] fix: ui: missing guests in resource tab when status is `paused` X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/> List-Post: <mailto:pve-devel@lists.proxmox.com> List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com> Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="us-ascii"; Format="flowed" Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com> high level comment: please separate a refactoring commit with the actual change having the sheet factored out is probably a good thing, but it's much easier to review if it's a separate patch, because one does not have to track multiple changes at once also some comments inline: On 5/20/25 10:05, Shan Shaji wrote: > When the guest status is set to `paused` the guest was not showing in > the resources tab. Also there were no option in the resources filter to > select the `paused` status under the status section. > > This commit fixes the issue by adding the `paused` status under the > status section in the resources filter sheet. Also the > `PveMobileResourceFilterSheet` has been splitted into muliple widgets > and moved to it's on file under the widgets folder. > > Signed-off-by: Shan Shaji <s.shaji@proxmox.com> > --- > lib/pages/main_layout_slim.dart | 179 +-------------- > .../pve_mobile_resource_filter_sheet.dart | 215 ++++++++++++++++++ > 2 files changed, 221 insertions(+), 173 deletions(-) > create mode 100644 lib/widgets/pve_mobile_resource_filter_sheet.dart > > diff --git a/lib/pages/main_layout_slim.dart b/lib/pages/main_layout_slim.dart > index ac5a6f9..5f4d34e 100644 > --- a/lib/pages/main_layout_slim.dart > +++ b/lib/pages/main_layout_slim.dart > @@ -26,6 +26,7 @@ import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart' > import 'package:pve_flutter_frontend/widgets/pve_file_selector_widget.dart'; > import 'package:pve_flutter_frontend/widgets/pve_guest_icon_widget.dart'; > import 'package:pve_flutter_frontend/widgets/pve_help_icon_button_widget.dart'; > +import 'package:pve_flutter_frontend/widgets/pve_mobile_resource_filter_sheet.dart'; > import 'package:pve_flutter_frontend/widgets/pve_resource_data_card_widget.dart'; > import 'package:pve_flutter_frontend/widgets/pve_resource_status_chip_widget.dart'; > import 'package:pve_flutter_frontend/widgets/pve_subscription_alert_dialog.dart'; > @@ -41,6 +42,7 @@ class MainLayoutSlim extends StatefulWidget { > > class _MainLayoutSlimState extends State<MainLayoutSlim> { > BehaviorSubject<int> pageSelector = BehaviorSubject.seeded(0); > + as with the other patch, this commits adds some unrelated new lines... > @override > Widget build(BuildContext context) { > final apiClient = Provider.of<ProxmoxApiClient>(context); > @@ -562,6 +564,7 @@ class PveNodeListTile extends StatelessWidget { > final String type; > final String? level; > final String? ip; > + also here > const PveNodeListTile( > {super.key, > required this.name, > @@ -569,6 +572,7 @@ class PveNodeListTile extends StatelessWidget { > required this.type, > this.level, > this.ip = ''}); > + and here > @override > Widget build(BuildContext context) { > return ListTile( > @@ -606,7 +610,7 @@ class MobileResourceOverview extends StatelessWidget { > }, > child: ColoredSafeArea( > child: Scaffold( > - endDrawer: _MobileResourceFilterSheet(), > + endDrawer: PveMobileResourceFilterSheet(), > appBar: AppBar( > automaticallyImplyLeading: false, > elevation: 0, > @@ -782,6 +786,7 @@ class AppbarSearchTextField extends StatefulWidget { > final ValueChanged<String>? onChanged; > > const AppbarSearchTextField({super.key, this.onChanged}); > + and here > @override > _AppbarSearchTextFieldState createState() => _AppbarSearchTextFieldState(); > } > @@ -844,178 +849,6 @@ class _AppbarSearchTextFieldState extends State<AppbarSearchTextField> { > } > } > > -class _MobileResourceFilterSheet extends StatelessWidget { > - @override > - Widget build(BuildContext context) { > - final rBloc = Provider.of<PveResourceBloc>(context); > - > - return ProxmoxStreamBuilder<PveResourceBloc, PveResourceState>( > - bloc: rBloc, > - builder: (context, state) => Drawer( > - child: SingleChildScrollView( > - child: Column( > - crossAxisAlignment: CrossAxisAlignment.start, > - children: <Widget>[ > - Padding( > - padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 0), > - child: ListTile( > - title: const Text( > - 'Filter Results', > - ), > - trailing: rBloc.isFiltered > - ? TextButton( > - onPressed: () => rBloc.events.add(ResetFilter()), > - child: Text( > - 'Reset', > - style: TextStyle( > - color: Theme.of(context).colorScheme.secondary, > - ), > - ), > - ) > - : null, > - ), > - ), > - const Divider( > - indent: 0, > - endIndent: 0, > - ), > - Padding( > - padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0), > - child: Column( > - children: [ > - const ListTile( > - title: Text( > - 'Type', > - style: TextStyle(fontWeight: FontWeight.bold), > - ), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'Nodes', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.typeFilter.contains('node'), > - onChanged: (v) => rBloc.events.add(FilterResources( > - typeFilter: addOrRemove(v!, 'node', state.typeFilter), > - )), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'Qemu', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.typeFilter.contains('qemu'), > - onChanged: (v) => rBloc.events.add(FilterResources( > - typeFilter: addOrRemove(v!, 'qemu', state.typeFilter), > - )), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'LXC', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.typeFilter.contains('lxc'), > - onChanged: (v) => rBloc.events.add(FilterResources( > - typeFilter: addOrRemove(v!, 'lxc', state.typeFilter), > - )), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'Storage', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.typeFilter.contains('storage'), > - onChanged: (v) => rBloc.events.add(FilterResources( > - typeFilter: > - addOrRemove(v!, 'storage', state.typeFilter), > - )), > - ), > - ], > - ), > - ), > - Padding( > - padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0), > - child: Column( > - children: [ > - const ListTile( > - title: Text( > - 'Status', > - style: TextStyle(fontWeight: FontWeight.bold), > - ), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'Online', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.statusFilter > - .contains(PveResourceStatusType.running), > - onChanged: (v) => rBloc.events.add(FilterResources( > - statusFilter: addOrRemove(v!, > - PveResourceStatusType.running, state.statusFilter), > - )), > - ), > - CheckboxListTile( > - dense: true, > - title: Text( > - 'Offline', > - style: TextStyle( > - color: Theme.of(context) > - .colorScheme > - .onSurface > - .withValues(alpha: 0.75)), > - ), > - value: state.statusFilter > - .contains(PveResourceStatusType.stopped), > - onChanged: (v) => rBloc.events.add(FilterResources( > - statusFilter: addOrRemove(v!, > - PveResourceStatusType.stopped, state.statusFilter), > - )), > - ), > - ], > - ), > - ) > - ], > - ), > - ), > - ), > - ); > - } > - > - BuiltSet<S> addOrRemove<S>(bool value, S element, BuiltSet<S> filter) { > - if (value) { > - return filter.rebuild((b) => b..add(element)); > - } else { > - return filter.rebuild((b) => b..remove(element)); > - } > - } > -} > - > class AppBarFilterIconButton extends StatelessWidget { > const AppBarFilterIconButton({super.key}); > > diff --git a/lib/widgets/pve_mobile_resource_filter_sheet.dart b/lib/widgets/pve_mobile_resource_filter_sheet.dart > new file mode 100644 > index 0000000..15b70f1 > --- /dev/null > +++ b/lib/widgets/pve_mobile_resource_filter_sheet.dart > @@ -0,0 +1,215 @@ > +import 'package:built_collection/built_collection.dart'; > +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_resource_bloc.dart'; > +import 'package:pve_flutter_frontend/states/pve_resource_state.dart'; > +import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart'; > + > +class PveMobileResourceFilterSheet extends StatelessWidget { > + const PveMobileResourceFilterSheet({super.key}); > + > + @override > + Widget build(BuildContext context) { > + final rBloc = Provider.of<PveResourceBloc>(context); > + return ProxmoxStreamBuilder<PveResourceBloc, PveResourceState>( > + bloc: rBloc, > + builder: (context, state) => Drawer( > + child: SingleChildScrollView( > + child: Column( > + crossAxisAlignment: CrossAxisAlignment.start, > + children: [ > + Padding( > + padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 0), > + child: ListTile( > + title: const Text( > + 'Filter Results', > + ), > + trailing: rBloc.isFiltered > + ? TextButton( > + onPressed: () => rBloc.events.add(ResetFilter()), > + child: Text( > + 'Reset', > + style: TextStyle( > + color: Theme.of(context).colorScheme.secondary, > + ), > + ), > + ) > + : null, > + ), > + ), > + const Divider(indent: 0, endIndent: 0), > + _PveFilterSheetSection( > + sectionTitle: 'Type', > + items: [ > + _ProxmoxResourceFilterListTile( > + title: 'Nodes', > + value: state.typeFilter.contains('node'), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + typeFilter: _addOrRemove(v!, 'node', state.typeFilter), > + ), > + ), > + ), > + _ProxmoxResourceFilterListTile( > + title: 'Qemu', > + value: state.typeFilter.contains('qemu'), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + typeFilter: _addOrRemove(v!, 'qemu', state.typeFilter), > + ), > + ), > + ), > + _ProxmoxResourceFilterListTile( > + title: 'LXC', > + value: state.typeFilter.contains('lxc'), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + typeFilter: _addOrRemove(v!, 'lxc', state.typeFilter), > + ), > + ), > + ), > + _ProxmoxResourceFilterListTile( > + title: 'Storage', > + value: state.typeFilter.contains('storage'), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + typeFilter: > + _addOrRemove(v!, 'storage', state.typeFilter), > + ), > + ), > + ), > + ], > + ), > + _PveFilterSheetSection( > + sectionTitle: 'Status', > + items: [ > + _ProxmoxResourceFilterListTile( > + title: 'Online', > + value: state.statusFilter > + .contains(PveResourceStatusType.running), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + statusFilter: _addOrRemove( > + v!, > + PveResourceStatusType.running, > + state.statusFilter, > + ), > + ), > + ), > + ), > + _ProxmoxResourceFilterListTile( > + title: 'Offline', > + value: state.statusFilter > + .contains(PveResourceStatusType.stopped), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + statusFilter: _addOrRemove( > + v!, > + PveResourceStatusType.stopped, > + state.statusFilter, > + ), > + ), > + ), > + ), > + _ProxmoxResourceFilterListTile( > + title: 'Paused', > + value: state.statusFilter > + .contains(PveResourceStatusType.paused), > + onChanged: (v) => rBloc.events.add( > + FilterResources( > + statusFilter: _addOrRemove( > + v!, > + PveResourceStatusType.paused, > + state.statusFilter, > + ), > + ), > + ), > + ), > + ], > + ) > + ], > + ), > + ), > + ), > + ); > + } > + > + BuiltSet<S> _addOrRemove<S>(bool value, S element, BuiltSet<S> filter) { > + if (value) { > + return filter.rebuild((b) => b..add(element)); > + } else { > + return filter.rebuild((b) => b..remove(element)); > + } > + } > +} > + > +class _PveFilterSheetSection extends StatelessWidget { > + const _PveFilterSheetSection({ > + required this.items, > + required this.sectionTitle, > + }); > + > + final List<Widget> items; > + final String sectionTitle; > + > + @override > + Widget build(BuildContext context) { > + return Padding( > + padding: const EdgeInsets.symmetric(horizontal: 8), > + child: Column( > + children: [ > + _ProxmoxFilterSheetHeader(title: sectionTitle), > + ...items, > + ], > + ), > + ); > + } > +} > + > +class _ProxmoxFilterSheetHeader extends StatelessWidget { > + const _ProxmoxFilterSheetHeader({ > + required this.title, > + }); > + > + final String title; > + > + @override > + Widget build(BuildContext context) { > + return ListTile( > + title: Text( > + title, > + style: TextStyle(fontWeight: FontWeight.bold), > + ), > + ); > + } > +} since you only use the _ProxmoxFilterSheetHeader once, you could also inline this in the _PveFilterSheetSection. That would save more code than having this as a separate widget. > + > +class _ProxmoxResourceFilterListTile extends StatelessWidget { > + const _ProxmoxResourceFilterListTile({ > + required this.title, > + this.onChanged, > + this.value, > + }); > + > + final String title; > + final ValueChanged<bool?>? onChanged; > + final bool? value; > + > + @override > + Widget build(BuildContext context) { > + return CheckboxListTile( > + dense: true, > + title: Text( > + title, > + style: TextStyle( > + color: Theme.of(context).colorScheme.onSurface.withValues( > + alpha: 0.75, > + ), > + ), > + ), > + value: value, > + onChanged: onChanged, > + ); > + } > +} _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel