From b807abab3513321398eff7343f788d383bd22c9d Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Tue, 30 Apr 2024 19:20:09 +0200 Subject: [PATCH] Add notebook and children page in drawer --- api/lib/models/event/item/database.dart | 83 ++++++-------------- api/lib/models/note/database.dart | 5 ++ api/lib/models/note/service.dart | 1 + api/lib/services/database.dart | 4 + app/lib/l10n/app_en.arb | 3 +- app/lib/pages/notes/navigator/children.dart | 64 +++++++++++++++ app/lib/pages/notes/navigator/drawer.dart | 53 +++++++++++-- app/lib/pages/notes/navigator/notebooks.dart | 41 ++++++++++ app/lib/pages/notes/note.dart | 7 +- app/lib/pages/notes/page.dart | 8 +- app/lib/pages/notes/view.dart | 2 +- 11 files changed, 197 insertions(+), 74 deletions(-) create mode 100644 app/lib/pages/notes/navigator/children.dart create mode 100644 app/lib/pages/notes/navigator/notebooks.dart diff --git a/api/lib/models/event/item/database.dart b/api/lib/models/event/item/database.dart index 9b1bbd98015..f5dd0601577 100644 --- a/api/lib/models/event/item/database.dart +++ b/api/lib/models/event/item/database.dart @@ -74,15 +74,11 @@ class CalendarItemDatabaseService extends CalendarItemService } if (start != null) { where = where == null ? 'start >= ?' : '$where AND start >= ?'; - whereArgs = whereArgs == null - ? [start.secondsSinceEpoch] - : [...whereArgs, start.secondsSinceEpoch]; + whereArgs = [...?whereArgs, start.secondsSinceEpoch]; } if (end != null) { where = where == null ? 'end <= ?' : '$where AND end <= ?'; - whereArgs = whereArgs == null - ? [end.secondsSinceEpoch] - : [...whereArgs, end.secondsSinceEpoch]; + whereArgs = [...?whereArgs, end.secondsSinceEpoch]; } if (date != null) { var startCalendarItem = date.onlyDate(); @@ -91,24 +87,15 @@ class CalendarItemDatabaseService extends CalendarItemService where = where == null ? '(start BETWEEN ? AND ? OR end BETWEEN ? AND ? OR (start <= ? AND end >= ?))' : '$where AND (start BETWEEN ? AND ? OR end BETWEEN ? AND ? OR (start <= ? AND end >= ?))'; - whereArgs = whereArgs == null - ? [ - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - ] - : [ - ...whereArgs, - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - startCalendarItem.secondsSinceEpoch, - endCalendarItem.secondsSinceEpoch, - ]; + whereArgs = [ + ...?whereArgs, + startCalendarItem.secondsSinceEpoch, + endCalendarItem.secondsSinceEpoch, + startCalendarItem.secondsSinceEpoch, + endCalendarItem.secondsSinceEpoch, + startCalendarItem.secondsSinceEpoch, + endCalendarItem.secondsSinceEpoch, + ]; } if (pending) { where = where == null @@ -119,60 +106,38 @@ class CalendarItemDatabaseService extends CalendarItemService where = where == null ? '(name LIKE ? OR description LIKE ?)' : '$where AND (name LIKE ? OR description LIKE ?)'; - whereArgs = whereArgs == null - ? ['%$search%', '%$search%'] - : [...whereArgs, '%$search%', '%$search%']; + whereArgs = [...?whereArgs, '%$search%', '%$search%']; } if (groupId != null) { - where = where == null ? 'groupId = ?' : '$where AND groupId = ?'; - whereArgs = whereArgs == null - ? [groupId.fullBytes] - : [...whereArgs, groupId.fullBytes]; + final statement = "(groupId = ? OR events.groupId = ?)"; + where = where == null ? statement : '$where AND $statement'; + whereArgs = [...?whereArgs, groupId.fullBytes, groupId.fullBytes]; } if (placeId != null) { - where = where == null ? 'placeId = ?' : '$where AND placeId = ?'; - whereArgs = whereArgs == null - ? [placeId.fullBytes] - : [...whereArgs, placeId.fullBytes]; + final statement = "(placeId = ? OR events.placeId = ?)"; + where = where == null ? statement : '$where AND $statement'; + whereArgs = [...?whereArgs, placeId.fullBytes, placeId.fullBytes]; } if (eventId != null) { where = where == null ? 'eventId = ?' : '$where AND eventId = ?'; - whereArgs = whereArgs == null - ? [eventId.fullBytes] - : [...whereArgs, eventId.fullBytes]; + whereArgs = [...?whereArgs, eventId.fullBytes]; } final result = await db?.query( "calendarItems LEFT JOIN events ON events.id = calendarItems.eventId", columns: [ "events.*", - "calendarItems.runtimeType AS calendarItemruntimeType", - "calendarItems.id AS calendarItemid", - "calendarItems.name AS calendarItemname", - "calendarItems.description AS calendarItemdescription", - "calendarItems.location AS calendarItemlocation", - "calendarItems.eventId AS calendarItemeventId", - "calendarItems.start AS calendarItemstart", - "calendarItems.end AS calendarItemend", - "calendarItems.status AS calendarItemstatus", - "calendarItems.repeatType AS calendarItemrepeatType", - "calendarItems.interval AS calendarIteminterval", - "calendarItems.variation AS calendarItemvariation", - "calendarItems.count AS calendarItemcount", - "calendarItems.until AS calendarItemuntil", - "calendarItems.exceptions AS calendarItemexceptions", - "calendarItems.autoGroupId AS calendarItemautoGroupId", - "calendarItems.searchStart AS calendarItemsearchStart", - "calendarItems.autoDuration AS calendarItemautoDuration", + "calendarItems.*", ], where: where, whereArgs: whereArgs, ); + const itemsPrefix = "calendarItems."; return result?.map((e) { return ConnectedModel( CalendarItem.fromDatabase(Map.fromEntries(e.entries - .where((element) => element.key.startsWith('calendarItem')) - .map((e) => MapEntry( - e.key.substring("calendarItem".length), e.value)))), + .where((element) => element.key.startsWith(itemsPrefix)) + .map((e) => + MapEntry(e.key.substring(itemsPrefix.length), e.value)))), e['id'] == null ? null : Event.fromDatabase(e), ); }).toList() ?? diff --git a/api/lib/models/note/database.dart b/api/lib/models/note/database.dart index 8899b0c9fd1..0554c991a3a 100644 --- a/api/lib/models/note/database.dart +++ b/api/lib/models/note/database.dart @@ -165,6 +165,7 @@ class NoteDatabaseService extends NoteService with TableService { int offset = 0, int limit = 50, Multihash? parent, + Multihash? notebook, Set labels = const {}, Set statuses = const { NoteStatus.todo, @@ -191,6 +192,10 @@ class NoteDatabaseService extends NoteService with TableService { where == null ? 'parentId IS NULL' : '$where AND parentId IS NULL'; } } + if (notebook != null) { + where = where == null ? 'notebookId = ?' : '$where AND notebookId = ?'; + whereArgs = [...?whereArgs, notebook.fullBytes]; + } var statusStatement = "status IN (${statuses.whereNotNull().map((e) => "'${e.name}'").join(',')})"; if (statuses.contains(null)) { diff --git a/api/lib/models/note/service.dart b/api/lib/models/note/service.dart index 1622e651aac..b3c00800905 100644 --- a/api/lib/models/note/service.dart +++ b/api/lib/models/note/service.dart @@ -10,6 +10,7 @@ abstract class NoteService extends ModelService { int offset = 0, int limit = 50, Multihash? parent, + Multihash? notebook, Set statuses = const { NoteStatus.todo, NoteStatus.inProgress, diff --git a/api/lib/services/database.dart b/api/lib/services/database.dart index 657bb68efd0..9199a757ff8 100644 --- a/api/lib/services/database.dart +++ b/api/lib/services/database.dart @@ -114,3 +114,7 @@ Multihash createUniqueMultihash() { List.generate(8, (i) => random.nextInt(255))); return Multihash(uuid); } + +Multihash createEmptyMultihash() { + return Multihash(Uint8List.fromList([])); +} diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 3549e0a752c..d80e8434c92 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -294,5 +294,6 @@ "standard": "Standard", "highContrast": "High contrast", "labels": "Labels", - "systemLocale": "System locale" + "systemLocale": "System locale", + "notebooks": "Notebooks" } \ No newline at end of file diff --git a/app/lib/pages/notes/navigator/children.dart b/app/lib/pages/notes/navigator/children.dart new file mode 100644 index 00000000000..6d60cf1ce56 --- /dev/null +++ b/app/lib/pages/notes/navigator/children.dart @@ -0,0 +1,64 @@ +part of 'drawer.dart'; + +class _NoteChildrenView extends StatefulWidget { + final SourcedModel? notebook; + final Multihash? parent; + + const _NoteChildrenView({ + this.parent, + this.notebook, + }); + + @override + State<_NoteChildrenView> createState() => _NoteChildrenViewState(); +} + +class _NoteChildrenViewState extends State<_NoteChildrenView> { + late final SourcedPagingController _controller; + + @override + void initState() { + super.initState(); + + _controller = SourcedPagingController( + context.read(), + ); + _controller.addFetchListener((source, service, offset, limit) async { + final notebook = widget.notebook; + if (notebook != null && notebook.source != source) return []; + return service.note?.getNotes( + offset: offset, + limit: limit, + parent: widget.parent, + notebook: notebook?.source == source ? notebook?.model?.id : null, + ); + }); + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + void didUpdateWidget(covariant _NoteChildrenView oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.parent != widget.parent) { + _controller.refresh(); + } + } + + @override + Widget build(BuildContext context) { + return PagedListView( + pagingController: _controller, + builderDelegate: buildMaterialPagedDelegate>( + _controller, + (ctx, item, index) => ListTile( + title: Text(item.model.name), + ), + ), + ); + } +} diff --git a/app/lib/pages/notes/navigator/drawer.dart b/app/lib/pages/notes/navigator/drawer.dart index 365bed307ca..16cf756eee0 100644 --- a/app/lib/pages/notes/navigator/drawer.dart +++ b/app/lib/pages/notes/navigator/drawer.dart @@ -4,35 +4,72 @@ import 'package:flow/pages/notes/label.dart'; import 'package:flow/widgets/builder_delegate.dart'; import 'package:flow_api/models/label/model.dart'; import 'package:flow_api/models/model.dart'; +import 'package:flow_api/models/note/model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:lib5/lib5.dart'; import 'package:material_leap/material_leap.dart'; +import 'package:material_leap/widgets.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; +part 'children.dart'; part 'labels.dart'; +part 'notebooks.dart'; class NotesNavigatorDrawer extends StatelessWidget { - final Multihash? selectedLabel; + final String source; + final Notebook? notebook; + final Multihash? note, selectedLabel; final LabelChangedCallback? onLabelChanged; const NotesNavigatorDrawer({ super.key, + this.source = '', + this.note, + this.notebook, this.selectedLabel, this.onLabelChanged, }); @override Widget build(BuildContext context) { - return ListView( - children: [ - _NoteLabelsView( - onChanged: onLabelChanged, - selected: selectedLabel, - ), - ], + final sourcedNotebook = + notebook == null ? null : SourcedModel(source, notebook); + return DefaultTabController( + length: 2, + child: Column( + children: [ + _NoteLabelsView( + onChanged: onLabelChanged, + selected: selectedLabel, + ), + TabBar(tabs: [ + HorizontalTab( + label: Text(AppLocalizations.of(context).notes), + icon: const PhosphorIcon(PhosphorIconsLight.article), + ), + HorizontalTab( + label: Text(AppLocalizations.of(context).notebooks), + icon: const PhosphorIcon(PhosphorIconsLight.fileArchive), + ), + ]), + Expanded( + child: TabBarView( + children: [ + _NoteChildrenView( + parent: note, + notebook: sourcedNotebook, + ), + _NotebooksView( + model: sourcedNotebook, + ), + ], + ), + ), + ], + ), ); } } diff --git a/app/lib/pages/notes/navigator/notebooks.dart b/app/lib/pages/notes/navigator/notebooks.dart new file mode 100644 index 00000000000..6fbc31eb70d --- /dev/null +++ b/app/lib/pages/notes/navigator/notebooks.dart @@ -0,0 +1,41 @@ +part of 'drawer.dart'; + +class _NotebooksView extends StatefulWidget { + final SourcedModel? model; + + const _NotebooksView({ + this.model, + }); + + @override + State<_NotebooksView> createState() => _NotebooksViewState(); +} + +class _NotebooksViewState extends State<_NotebooksView> { + late final SourcedPagingController _controller; + + @override + void initState() { + super.initState(); + + _controller = SourcedPagingController( + context.read(), + ); + _controller.addFetchListener((source, service, offset, limit) async { + return []; + }); + } + + @override + Widget build(BuildContext context) { + return PagedListView( + pagingController: _controller, + builderDelegate: buildMaterialPagedDelegate>( + _controller, + (ctx, item, index) => ListTile( + title: Text(item.model.name), + ), + ), + ); + } +} diff --git a/app/lib/pages/notes/note.dart b/app/lib/pages/notes/note.dart index 4f681b1f815..09a61d40864 100644 --- a/app/lib/pages/notes/note.dart +++ b/app/lib/pages/notes/note.dart @@ -13,7 +13,12 @@ class NoteDialog extends StatefulWidget { final String? source; final Note? note; final bool create; - const NoteDialog({super.key, this.create = false, this.note, this.source}); + const NoteDialog({ + super.key, + this.create = false, + this.note, + this.source, + }); @override State createState() => _NoteDialogState(); diff --git a/app/lib/pages/notes/page.dart b/app/lib/pages/notes/page.dart index b172ae69e5a..3b1c2d94e8f 100644 --- a/app/lib/pages/notes/page.dart +++ b/app/lib/pages/notes/page.dart @@ -1,10 +1,9 @@ -import 'dart:typed_data'; - import 'package:flow/pages/notes/card.dart'; import 'package:flow/pages/notes/navigator/drawer.dart'; import 'package:flow/pages/notes/note.dart'; import 'package:flow/widgets/builder_delegate.dart'; import 'package:flow/widgets/navigation.dart'; +import 'package:flow_api/services/database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -47,6 +46,7 @@ class _NotesPageState extends State { return FlowNavigation( title: AppLocalizations.of(context).notes, endDrawer: NotesNavigatorDrawer( + note: widget.parent?.model, selectedLabel: _filter.selectedLabel, onLabelChanged: (value, add) { final source = value.source; @@ -167,7 +167,7 @@ class _NotesBodyViewState extends State { statuses: _filter.statuses, parent: widget.parent?.source == source ? widget.parent?.model - : Multihash(Uint8List.fromList([])), + : createEmptyMultihash(), search: widget.search, ) : await service.note?.getNotes( @@ -176,7 +176,7 @@ class _NotesBodyViewState extends State { statuses: _filter.statuses, parent: widget.parent?.source == source ? widget.parent?.model - : Multihash(Uint8List.fromList([])), + : createEmptyMultihash(), search: widget.search); if (notes == null) return null; if (source != widget.parent?.source) return notes; diff --git a/app/lib/pages/notes/view.dart b/app/lib/pages/notes/view.dart index b6f4f272d5d..c5e9c0d1758 100644 --- a/app/lib/pages/notes/view.dart +++ b/app/lib/pages/notes/view.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flow/helpers/sourced_paging_controller.dart'; +import 'package:flow/pages/notes/select.dart'; import 'package:flow/widgets/markdown_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -17,7 +18,6 @@ import 'package:flow_api/services/source.dart'; import '../../cubits/flow.dart'; import 'label.dart'; -import 'select.dart'; class NoteView extends StatefulWidget { final String source;