From 4ed622abfe4b83861b605cbc004cb9e6d35c44ec Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Wed, 21 Jun 2023 14:01:31 +0200 Subject: [PATCH] Add folder structure to pages, closes #398 --- app/lib/bloc/document_state.dart | 13 ++ app/lib/views/pages.dart | 161 ++++++++++++++---- .../metadata/android/en-US/changelogs/65.txt | 1 + 3 files changed, 143 insertions(+), 32 deletions(-) diff --git a/app/lib/bloc/document_state.dart b/app/lib/bloc/document_state.dart index 79acb80199ad..e6ec2efb46ab 100644 --- a/app/lib/bloc/document_state.dart +++ b/app/lib/bloc/document_state.dart @@ -10,6 +10,13 @@ abstract class DocumentState extends Equatable { @override List get props => []; + + NoteData? get data => null; + DocumentPage? get page => null; + DocumentInfo? get info => null; + String? get pageName => null; + FileMetadata? get metadata => null; + AssetService? get assetService => null; } class DocumentLoadInProgress extends DocumentState {} @@ -24,11 +31,17 @@ class DocumentLoadFailure extends DocumentState { } abstract class DocumentLoaded extends DocumentState { + @override final NoteData data; + @override final DocumentPage page; + @override final DocumentInfo info; + @override final String pageName; + @override final FileMetadata metadata; + @override final AssetService assetService; set page(DocumentPage page) => data.setPage(page); diff --git a/app/lib/views/pages.dart b/app/lib/views/pages.dart index 6a63ab6c8898..b55664166a04 100644 --- a/app/lib/views/pages.dart +++ b/app/lib/views/pages.dart @@ -9,12 +9,22 @@ import 'package:phosphor_flutter/phosphor_flutter.dart'; import '../dialogs/delete.dart'; +typedef _PageEntity = ({ + String path, + String name, + bool isFile, +}); + class PagesView extends StatelessWidget { const PagesView({super.key}); @override Widget build(BuildContext context) { + final TextEditingController locationController = TextEditingController(); return BlocBuilder( + buildWhen: (previous, current) => + previous.data != current.data || + previous.pageName != current.pageName, builder: (context, state) { if (state is! DocumentLoadSuccess) return const SizedBox.shrink(); final currentName = state.pageName; @@ -26,39 +36,72 @@ class PagesView extends StatelessWidget { final pages = snapshot.data!.getPages(); return Stack( children: [ - ListView.builder( - itemCount: pages.length, - itemBuilder: (BuildContext context, int index) { - final page = pages[index]; - return ListTile( - title: Text(page), - selected: page == currentName, - onTap: () => context - .read() - .add(PageChanged(page)), - trailing: MenuAnchor( - builder: defaultMenuButton(), - menuChildren: [ - MenuItemButton( - leadingIcon: const PhosphorIcon( - PhosphorIconsLight.trash), - onPressed: currentName == page - ? null - : () async { - final result = await showDialog( - context: context, - builder: (context) => - const DeleteDialog()); - if (result != true) return; - snapshot.data?.removePage(page); - }, - child: - Text(AppLocalizations.of(context).delete), - ) - ], + Column( + children: [ + const SizedBox(height: 8), + TextFormField( + controller: locationController, + decoration: InputDecoration( + labelText: AppLocalizations.of(context).location, + suffixIcon: IconButton( + icon: + const PhosphorIcon(PhosphorIconsLight.arrowUp), + onPressed: () { + final paths = locationController.text.split('/'); + if (paths.length <= 1) { + locationController.text = ''; + return; + } + paths.removeLast(); + locationController.text = paths.join('/'); + }, ), - ); - }), + border: const OutlineInputBorder(), + ), + ), + const SizedBox(height: 8), + Expanded( + child: ValueListenableBuilder( + valueListenable: locationController, + builder: (context, value, child) { + final query = + value.text.isEmpty ? '' : '${value.text}/'; + final queried = pages + .where((element) => element.startsWith(query)) + .map((e) => ( + path: e, + name: e.substring(query.length), + isFile: true + )) + .toList(); + final files = queried.where( + (element) => !element.name.contains('/')); + final folders = queried + .where((element) => element.name.contains('/')) + .map((e) => e.name.split('/').first) + .map((e) { + var path = value.text; + if (path.isNotEmpty) path += '/'; + path += e; + return (path: path, name: e, isFile: false); + }).toSet(); + final all = [...folders, ...files]; + return ListView.builder( + itemCount: all.length, + itemBuilder: (BuildContext context, int index) { + final entity = all[index]; + return _PageEntityListTile( + entity: entity, + selected: entity.path == currentName, + locationController: locationController, + data: snapshot.data!, + ); + }); + }, + ), + ), + ], + ), Align( alignment: Alignment.bottomCenter, child: Padding( @@ -89,3 +132,57 @@ class PagesView extends StatelessWidget { ); } } + +class _PageEntityListTile extends StatelessWidget { + const _PageEntityListTile({ + required this.entity, + required this.selected, + required this.locationController, + required this.data, + }); + + final _PageEntity entity; + final bool selected; + final NoteData data; + final TextEditingController locationController; + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text(entity.name), + selected: selected, + leading: Icon(entity.isFile + ? PhosphorIconsLight.file + : PhosphorIconsLight.folderSimple), + onTap: () { + if (entity.isFile) { + context.read().add(PageChanged(entity.path)); + } else { + locationController.text = entity.path; + } + }, + trailing: entity.isFile + ? MenuAnchor( + builder: defaultMenuButton(), + menuChildren: [ + MenuItemButton( + leadingIcon: const PhosphorIcon(PhosphorIconsLight.trash), + onPressed: selected + ? null + : () async { + final result = await showDialog( + context: context, + builder: (context) => const DeleteDialog()); + if (result != true) { + return; + } + data.removePage(entity.path); + }, + child: Text(AppLocalizations.of(context).delete), + ) + ], + ) + : null, + ); + } +} diff --git a/fastlane/metadata/android/en-US/changelogs/65.txt b/fastlane/metadata/android/en-US/changelogs/65.txt index db3a0706b2e0..1f867d8d5851 100644 --- a/fastlane/metadata/android/en-US/changelogs/65.txt +++ b/fastlane/metadata/android/en-US/changelogs/65.txt @@ -3,6 +3,7 @@ * Add move elements in the z axis ([#396](https://github.com/LinwoodDev/Butterfly/issues/396)) * Add saving indicator ([#402](https://github.com/LinwoodDev/Butterfly/issues/402)) * Add pack remote directory ([#389](https://github.com/LinwoodDev/Butterfly/issues/389)) +* Add folder structure to pages ([#398](https://github.com/LinwoodDev/Butterfly/issues/398)) * Use delay on reorder item for painters * Use delay on files in home page * Migrate selection context menu to new menu button widget