diff --git a/client/lib/ui/destination/destination_picker.dart b/client/lib/ui/destination/destination_picker.dart new file mode 100644 index 00000000..0121c854 --- /dev/null +++ b/client/lib/ui/destination/destination_picker.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:phylum/libphylum/phylum_account.dart'; +import 'package:phylum/ui/explorer/resource_icon_extension.dart'; +import 'package:provider/provider.dart'; + +class DestinationPicker extends StatefulWidget { + final String startingFolderId; + const DestinationPicker({super.key, required this.startingFolderId}); + + @override + State createState() => _DestinationPickerState(); + + static Future pick(BuildContext context, String startingFolderId) { + return showDialog( + context: context, + builder: (context) => DestinationPicker(startingFolderId: startingFolderId), + ); + } +} + +class _DestinationPickerState extends State { + late String folderId; + + @override + void initState() { + super.initState(); + folderId = widget.startingFolderId; + } + + @override + Widget build(BuildContext context) { + debugPrint('build $folderId'); + return Scaffold( + appBar: AppBar(title: Text('Pick Destination')), + body: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: DestinationFolderView( + id: folderId, + onFolderSelected: (id) => setState(() => folderId = id), + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton(onPressed: Navigator.of(context).pop, child: Text('Cancel')), + ElevatedButton(onPressed: folderId == widget.startingFolderId ? null : () => Navigator.of(context).pop(folderId), child: Text('OK')), + ], + ), + ) + ], + ), + ); + } +} + +class DestinationFolderView extends StatelessWidget { + final String id; + final Function(String) onFolderSelected; + + DestinationFolderView({required this.id, required this.onFolderSelected}) : super(key: ValueKey(id)); + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: context.read().resourceRepository.watchChildren(id), + builder: (context, snapshot) { + final data = snapshot.data; + if (data == null) return Center(child: CircularProgressIndicator()); + return ListView.builder( + itemBuilder: (BuildContext context, int index) { + final r = data[index]; + return ListTile( + visualDensity: VisualDensity.compact, + leading: r.getIcon(), + title: Text(r.name), + enabled: r.dir, + onTap: () { + onFolderSelected(r.id); + }, + ); + }, + itemCount: data.length, + ); + }); + } +} diff --git a/client/lib/ui/menu/menu_option.dart b/client/lib/ui/menu/menu_option.dart index d83b674d..7a35694d 100644 --- a/client/lib/ui/menu/menu_option.dart +++ b/client/lib/ui/menu/menu_option.dart @@ -10,6 +10,7 @@ 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/destination/destination_picker.dart'; import 'package:phylum/ui/explorer/resource_info_view.dart'; import 'package:phylum/util/dialogs.dart'; import 'package:provider/provider.dart'; @@ -48,8 +49,9 @@ void handleOption(BuildContext context, Iterable resources, MenuOption } break; case MenuOption.move: - break; case MenuOption.copy: + final parent = resources.first.parent!; + await DestinationPicker.pick(context, parent); break; case MenuOption.delete: final name = resources.length == 1 ? resources.first.name : '${resources.length} items'; diff --git a/client/pubspec.lock b/client/pubspec.lock index fd28ae2f..47a7723a 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -964,7 +964,7 @@ packages: source: hosted version: "1.0.0" uuid: - dependency: "direct main" + dependency: transitive description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff