From f202ccdd7524368772b21abb342f6854f9108375 Mon Sep 17 00:00:00 2001 From: Nutcake Date: Sat, 17 Jun 2023 19:28:23 +0200 Subject: [PATCH] Improve inventory selection logic --- lib/clients/inventory_client.dart | 4 +- lib/widgets/inventory/inventory_browser.dart | 131 +++++++++++------- .../inventory/path_inventory_tile.dart | 17 ++- 3 files changed, 99 insertions(+), 53 deletions(-) diff --git a/lib/clients/inventory_client.dart b/lib/clients/inventory_client.dart index 3482e6a..91df967 100644 --- a/lib/clients/inventory_client.dart +++ b/lib/clients/inventory_client.dart @@ -82,7 +82,9 @@ class InventoryClient extends ChangeNotifier { childDir.children.addAll(records.map((record) => NeosDirectory.fromRecord(record: record, parent: childDir))); return childDir; }, - ); + ).onError((error, stackTrace) { + return dir; + }); } notifyListeners(); } diff --git a/lib/widgets/inventory/inventory_browser.dart b/lib/widgets/inventory/inventory_browser.dart index 4f7809b..e81fce8 100644 --- a/lib/widgets/inventory/inventory_browser.dart +++ b/lib/widgets/inventory/inventory_browser.dart @@ -132,48 +132,25 @@ class _InventoryBrowserState extends State with AutomaticKeepA mainAxisSpacing: 8), itemBuilder: (context, index) { final record = paths[index]; - return PathInventoryTile( - record: record, - onPressed: () { - iClient.navigateTo(record); - }, - ); - }, - ), - const SizedBox( - height: 8, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: GridView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: objects.length, - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 256, - childAspectRatio: 1, - crossAxisSpacing: 8, - mainAxisSpacing: 8, - ), - itemBuilder: (context, index) { - final record = objects[index]; - return ObjectInventoryTile( + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 3.0), + child: PathInventoryTile( record: record, selected: _selectedIds.contains(record.id), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PhotoView( - minScale: PhotoViewComputedScale.contained, - imageProvider: - CachedNetworkImageProvider(Aux.neosDbToHttp(record.thumbnailUri)), - heroAttributes: PhotoViewHeroAttributes(tag: record.id), - ), - ), - ); - }, - onLongPress: () async { + onTap: _selectedIds.isEmpty + ? () { + iClient.navigateTo(record); + } + : () { + setState(() { + if (_selectedIds.contains(record.id)) { + _selectedIds.remove(record.id); + } else { + _selectedIds.add(record.id); + } + }); + }, + onLongPress: () { setState(() { if (_selectedIds.contains(record.id)) { _selectedIds.remove(record.id); @@ -182,9 +159,63 @@ class _InventoryBrowserState extends State with AutomaticKeepA } }); }, - ); - }, + ), + ); + }, + ), + const SizedBox( + height: 8, + ), + GridView.builder( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: objects.length, + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 256, + childAspectRatio: 1, + crossAxisSpacing: 8, + mainAxisSpacing: 8, ), + itemBuilder: (context, index) { + final record = objects[index]; + return ObjectInventoryTile( + record: record, + selected: _selectedIds.contains(record.id), + onTap: _selectedIds.isEmpty + ? () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PhotoView( + minScale: PhotoViewComputedScale.contained, + imageProvider: CachedNetworkImageProvider( + Aux.neosDbToHttp(record.thumbnailUri)), + heroAttributes: PhotoViewHeroAttributes(tag: record.id), + ), + ), + ); + } + : () async { + setState(() { + if (_selectedIds.contains(record.id)) { + _selectedIds.remove(record.id); + } else { + _selectedIds.add(record.id); + } + }); + }, + onLongPress: () async { + setState(() { + if (_selectedIds.contains(record.id)) { + _selectedIds.remove(record.id); + } else { + _selectedIds.add(record.id); + } + }); + }, + ); + }, ), ], ), @@ -192,18 +223,22 @@ class _InventoryBrowserState extends State with AutomaticKeepA alignment: Alignment.topCenter, child: AnimatedSwitcher( duration: const Duration(milliseconds: 250), - child: snapshot.connectionState == ConnectionState.waiting ? const LinearProgressIndicator() : null, + child: snapshot.connectionState == ConnectionState.waiting + ? const LinearProgressIndicator() + : null, ), ), Align( alignment: Alignment.topCenter, child: AnimatedSwitcher( duration: const Duration(milliseconds: 250), - child: snapshot.connectionState == ConnectionState.waiting ? Container( - width: double.infinity, - height: double.infinity, - color: Colors.black38, - ) : null, + child: snapshot.connectionState == ConnectionState.waiting + ? Container( + width: double.infinity, + height: double.infinity, + color: Colors.black38, + ) + : null, ), ) ], diff --git a/lib/widgets/inventory/path_inventory_tile.dart b/lib/widgets/inventory/path_inventory_tile.dart index 003d3fa..bae26e5 100644 --- a/lib/widgets/inventory/path_inventory_tile.dart +++ b/lib/widgets/inventory/path_inventory_tile.dart @@ -3,20 +3,29 @@ import 'package:contacts_plus_plus/widgets/formatted_text.dart'; import 'package:flutter/material.dart'; class PathInventoryTile extends StatelessWidget { - const PathInventoryTile({required this.record, required this.onPressed, super.key}); + const PathInventoryTile({required this.record, this.selected = false, this.onTap, this.onLongPress, super.key}); final Record record; - final Function() onPressed; + final Function()? onTap; + final Function()? onLongPress; + final bool selected; @override Widget build(BuildContext context) { return OutlinedButton.icon( style: TextButton.styleFrom( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + side: BorderSide( + color: selected ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, + width: 1, + ), foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer, alignment: Alignment.centerLeft, ), - onPressed: onPressed, + onLongPress: onLongPress, + onPressed: onTap, icon: record.recordType == RecordType.directory ? const Icon(Icons.folder) : const Icon(Icons.link), label: FormattedText( record.formattedName,