Skip to content

Commit

Permalink
Add folder structure to pages, closes #398
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Jun 21, 2023
1 parent f65343d commit 4ed622a
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 32 deletions.
13 changes: 13 additions & 0 deletions app/lib/bloc/document_state.dart
Expand Up @@ -10,6 +10,13 @@ abstract class DocumentState extends Equatable {

@override
List<Object?> 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 {}
Expand All @@ -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);
Expand Down
161 changes: 129 additions & 32 deletions app/lib/views/pages.dart
Expand Up @@ -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<DocumentBloc, DocumentState>(
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;
Expand All @@ -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<DocumentBloc>()
.add(PageChanged(page)),
trailing: MenuAnchor(
builder: defaultMenuButton(),
menuChildren: [
MenuItemButton(
leadingIcon: const PhosphorIcon(
PhosphorIconsLight.trash),
onPressed: currentName == page
? null
: () async {
final result = await showDialog<bool>(
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<TextEditingValue>(
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(
Expand Down Expand Up @@ -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<DocumentBloc>().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<bool>(
context: context,
builder: (context) => const DeleteDialog());
if (result != true) {
return;
}
data.removePage(entity.path);
},
child: Text(AppLocalizations.of(context).delete),
)
],
)
: null,
);
}
}
1 change: 1 addition & 0 deletions fastlane/metadata/android/en-US/changelogs/65.txt
Expand Up @@ -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
Expand Down

0 comments on commit 4ed622a

Please sign in to comment.