[client] refactor menu options

This commit is contained in:
Abhishek Shroff
2024-11-22 00:22:21 +05:30
parent a710ab003e
commit cefd7d6444
7 changed files with 164 additions and 148 deletions
@@ -36,7 +36,7 @@ class BookmarkRepository {
}
Selectable<int> bookmarksCount(Iterable<String> ids) {
return account.db.bookmarks.count(where: (b) => b.resourceId.isIn(ids));
return account.db.bookmarks.count(where: (b) => b.resourceId.isIn(ids) & b.deleted.equals(false));
}
Future<ApiResult> refresh() async {
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:phylum/app_shortcuts.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/explorer/explorer_view_controller.dart';
import 'package:phylum/ui/explorer/menu_options.dart';
import 'package:phylum/ui/menu/menu_option.dart';
import 'package:phylum/ui/menu/option_groups.dart';
import 'package:provider/provider.dart';
class ExplorerGestureHandler extends StatelessWidget {
@@ -25,13 +27,16 @@ class ExplorerGestureHandler extends StatelessWidget {
details.globalPosition.dx,
details.globalPosition.dy,
);
final resources = [resource];
final items = await buildPopupMenuItems(context.read<PhylumAccount>(), parentFolderOptions, resources);
if (!context.mounted) return;
final selection = await showMenu(
context: context,
position: position,
items: buildExplorerPopupMenuItems(context, [resource]).toList(growable: false),
items: items.toList(growable: false),
);
if (context.mounted && selection != null) {
handleResourceOption(context, [resource], selection);
handleOption(context, resources, selection);
}
},
child: child,
@@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/ui/explorer/resource_icon_extension.dart';
import 'package:phylum/ui/menu/option_groups.dart';
import 'package:provider/provider.dart';
import 'explorer_view_controller.dart';
import 'menu_options.dart';
import '../menu/bottom_sheet.dart';
class ResourceDetailsRow extends StatelessWidget {
final Resource r;
@@ -47,7 +48,7 @@ class ResourceDetailsRow extends StatelessWidget {
softWrap: false,
overflow: TextOverflow.fade,
),
trailing: ResourceOptionsButton(r: r),
trailing: IconButton(icon: Icon(Icons.adaptive.more), onPressed: () => showMenuOptionsBottomSheet(context, resourceOptions, [r])),
shape: RoundedRectangleBorder(side: border, borderRadius: BorderRadius.circular(4.0)),
selected: highlight,
selectedColor: dim ? theme.colorScheme.primary.withOpacity(0.75) : null,
@@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:phylum/app_shortcuts.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/explorer/explorer_view_controller.dart';
import 'package:phylum/ui/explorer/resource_drop_and_drop.dart';
import 'package:phylum/ui/explorer/menu_options.dart';
import 'package:phylum/ui/menu/menu_option.dart';
import 'package:phylum/ui/menu/option_groups.dart';
import 'package:provider/provider.dart';
class ResourceItemGestureHandler extends StatefulWidget {
@@ -78,13 +80,15 @@ class _ResourceItemGestureHandlerState extends State<ResourceItemGestureHandler>
details.globalPosition.dy,
);
final resources = state.selected;
final items = await buildPopupMenuItems(context.read<PhylumAccount>(), resourceOptions, resources);
if (!context.mounted) return;
final selection = await showMenu(
context: context,
position: position,
items: buildChildPopupMenuItems(context, resources).toList(growable: false),
items: items.toList(growable: false),
);
if (context.mounted && selection != null) {
handleResourceOption(context, resources, selection);
handleOption(context, resources, selection);
}
},
onDoubleTap: () {
+47
View File
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/explorer/resource_icon_extension.dart';
import 'package:phylum/ui/menu/menu_option.dart';
import 'package:provider/provider.dart';
import 'option_groups.dart';
void showMenuOptionsBottomSheet(BuildContext context, MenuOptionGroups options, Iterable<Resource> resources) async {
final items = await buildPopupMenuItems(context.read<PhylumAccount>(), options, resources);
if (!context.mounted) return;
final selected = await showModalBottomSheet(
context: context,
builder: (context) => ListView(
shrinkWrap: true,
children: [
DecoratedBox(
position: DecorationPosition.foreground,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: resources.length == 1
? ListTile(
leading: resources.first.getIcon(),
title: Text(
resources.first.name,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
),
)
: ListTile(
title: Text('${resources.length} items'),
),
),
...items,
],
));
if (!context.mounted || selected == null) {
return;
}
handleOption(context, resources, selected);
}
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:phylum/app_shortcuts.dart';
import 'package:phylum/integrations/download_manager.dart';
@@ -8,7 +10,6 @@ import 'package:phylum/libphylum/actions/bookmark_remove_action.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/common/responsive_dialog.dart';
import 'package:phylum/ui/explorer/resource_icon_extension.dart';
import 'package:phylum/ui/explorer/resource_info_view.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:provider/provider.dart';
@@ -16,8 +17,8 @@ import 'package:provider/provider.dart';
enum MenuOption {
details(Icons.info_outline, 'Details', isSingle),
download(Icons.download, 'Download', isFilesOnly),
bookmarkAdd(Icons.bookmark_add_outlined, 'Add To Bookmarks', all),
bookmarkRemove(Icons.bookmark_remove_outlined, 'Remove From Bookmarks', all),
bookmarkAdd(Icons.bookmark_add_outlined, 'Add To Bookmarks', notAllBookmarked),
bookmarkRemove(Icons.bookmark_remove, 'Remove From Bookmarks', allBookmarked),
rename(Icons.drive_file_rename_outline, 'Rename', isSingle),
move(Icons.drive_file_move_outlined, 'Move To', all),
copy(Icons.copy, 'Copy To', all),
@@ -33,79 +34,10 @@ enum MenuOption {
final IconData icon;
final String text;
final bool Function(PhylumAccount account, Iterable<Resource> resources) filter;
final FutureOr<bool> Function(PhylumAccount account, Iterable<Resource> resources) filter;
}
const resourceOptionsGroups = [
[
MenuOption.download,
MenuOption.rename,
MenuOption.move,
MenuOption.copy,
MenuOption.delete,
],
[
MenuOption.bookmarkAdd,
MenuOption.bookmarkRemove,
MenuOption.permissions,
MenuOption.publinks,
],
[
MenuOption.details,
],
];
final explorerOptionsGroups = [
[
MenuOption.newFolder,
MenuOption.uploadFiles,
MenuOption.uploadFolders,
],
[
MenuOption.bookmarkAdd,
MenuOption.bookmarkRemove,
MenuOption.permissions,
MenuOption.publinks,
],
[
MenuOption.details,
]
];
bool isSingle(PhylumAccount account, Iterable<Resource> resources) => resources.length == 1;
bool none(PhylumAccount account, Iterable<Resource> resources) => false;
bool all(PhylumAccount accout, Iterable<Resource> resources) => true;
bool isFilesOnly(PhylumAccount account, Iterable<Resource> resources) => resources.every((r) => !r.dir);
class ResourceOptionsButton extends StatelessWidget {
final Resource r;
const ResourceOptionsButton({super.key, required this.r});
@override
Widget build(BuildContext context) {
final resources = [r];
return MediaQuery.sizeOf(context).width < 800
? IconButton(icon: Icon(Icons.adaptive.more), onPressed: () => showResourceOptions(context, resources))
: PopupMenuButton(
itemBuilder: (context) => buildChildPopupMenuItems(context, resources).toList(growable: false),
onSelected: (o) => handleResourceOption(context, resources, o),
);
}
}
void showResourceOptions(BuildContext context, Iterable<Resource> resources) async {
final option = await showModalBottomSheet(context: context, builder: (context) => ResourceOptionsList(resources: resources));
if (!context.mounted || option == null) {
return;
}
handleResourceOption(context, resources, option);
}
void handleResourceOption(BuildContext context, Iterable<Resource> resources, MenuOption option) async {
void handleOption(BuildContext context, Iterable<Resource> resources, MenuOption option) async {
final account = context.read<PhylumAccount>();
switch (option) {
case MenuOption.rename:
@@ -172,73 +104,15 @@ void handleResourceOption(BuildContext context, Iterable<Resource> resources, Me
}
}
class ResourceOptionsList extends StatelessWidget {
final Iterable<Resource> resources;
bool isSingle(PhylumAccount account, Iterable<Resource> resources) => resources.length == 1;
const ResourceOptionsList({super.key, required this.resources});
bool none(PhylumAccount account, Iterable<Resource> resources) => false;
@override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
children: [
DecoratedBox(
position: DecorationPosition.foreground,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: resources.length == 1
? ListTile(
leading: resources.first.getIcon(),
title: Text(
resources.first.name,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
),
)
: ListTile(
title: Text('${resources.length} items'),
),
),
..._buildItems(context, resourceOptionsGroups, resources)
],
);
}
}
bool all(PhylumAccount accout, Iterable<Resource> resources) => true;
Iterable<PopupMenuEntry> _buildItems(BuildContext context, Iterable<Iterable<MenuOption>> options, Iterable<Resource> resources) {
final account = context.read<PhylumAccount>();
return options
.map((group) => group.map((o) => PopupMenuItem(
value: o,
enabled: o.filter(account, resources),
child: ListTile(
visualDensity: VisualDensity.adaptivePlatformDensity,
leading: Icon(o.icon),
title: Text(o.text),
),
)))
.fold(
<PopupMenuEntry>[],
(acc, e) {
if (e.isEmpty) {
return acc;
}
if (acc.isEmpty) {
return e;
}
return [...acc, PopupMenuDivider(), ...e];
},
);
}
bool isFilesOnly(PhylumAccount account, Iterable<Resource> resources) => resources.every((r) => !r.dir);
Iterable<PopupMenuEntry> buildChildPopupMenuItems(BuildContext context, Iterable<Resource> resources) {
return _buildItems(context, resourceOptionsGroups, resources);
}
Future<bool> notAllBookmarked(PhylumAccount account, Iterable<Resource> resources) => allBookmarked(account, resources).then((b) => !b);
Iterable<PopupMenuEntry> buildExplorerPopupMenuItems(BuildContext context, Iterable<Resource> resources) {
return _buildItems(context, explorerOptionsGroups, resources);
}
Future<bool> allBookmarked(PhylumAccount account, Iterable<Resource> resources) =>
account.bookmarkRepository.bookmarksCount(resources.map((r) => r.id)).getSingle().then((c) => c == resources.length);
+85
View File
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'menu_option.dart';
typedef MenuOptionGroups = Iterable<Iterable<MenuOption>>;
const resourceOptions = [
[
MenuOption.download,
MenuOption.rename,
MenuOption.move,
MenuOption.copy,
MenuOption.delete,
],
[
MenuOption.bookmarkAdd,
MenuOption.bookmarkRemove,
MenuOption.permissions,
MenuOption.publinks,
],
[
MenuOption.details,
],
];
final parentFolderOptions = [
[
MenuOption.newFolder,
MenuOption.uploadFiles,
MenuOption.uploadFolders,
],
[
MenuOption.bookmarkAdd,
MenuOption.bookmarkRemove,
MenuOption.permissions,
MenuOption.publinks,
],
[
MenuOption.details,
]
];
Future<Iterable<PopupMenuEntry<MenuOption>>> buildPopupMenuItems(
PhylumAccount account,
MenuOptionGroups options,
Iterable<Resource> resources, [
bool hideDisabled = true,
]) async {
final availableOptions = await Future.wait(
options.map(
(group) async => Future.wait(
group.map((o) async {
final enabled = await o.filter(account, resources);
return hideDisabled && !enabled
? null
: PopupMenuItem(
value: o,
enabled: enabled,
child: ListTile(
visualDensity: VisualDensity.adaptivePlatformDensity,
leading: Icon(o.icon),
title: Text(o.text),
),
);
}),
).then((items) => items.whereType<PopupMenuEntry<MenuOption>>()),
),
);
final m = availableOptions.fold(
<PopupMenuEntry<MenuOption>>[],
(acc, e) {
if (e.isEmpty) {
return acc;
}
if (acc.isEmpty) {
return e.toList(growable: false);
}
return [...acc, PopupMenuDivider(), ...e];
},
);
return m;
}