From 8c310aa2cccf82735fc7ccef5a961846543561c5 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 29 Oct 2023 20:06:55 +0100 Subject: [PATCH 1/2] Upgrade dropdowns to material 3 --- app/lib/pages/calendar/item.dart | 39 +++++------ app/lib/pages/notes/page.dart | 59 +++++++++------- app/lib/widgets/source_dropdown.dart | 27 +++----- app/pubspec.lock | 68 +++++++++---------- app/pubspec.yaml | 4 +- .../metadata/android/en-US/changelogs/6.txt | 1 + 6 files changed, 97 insertions(+), 101 deletions(-) diff --git a/app/lib/pages/calendar/item.dart b/app/lib/pages/calendar/item.dart index 053bf864370..59fb3e76b82 100644 --- a/app/lib/pages/calendar/item.dart +++ b/app/lib/pages/calendar/item.dart @@ -217,32 +217,25 @@ class _CalendarItemDialogState extends State { }, ), const SizedBox(height: 16), - DropdownButtonFormField( - value: _item.status, - items: EventStatus.values - .map>((value) { - return DropdownMenuItem( - value: value, - child: Row( - children: [ - PhosphorIcon( - value.icon(PhosphorIconsStyle.light), - color: value.getColor()), - const SizedBox(width: 8), - Text(value.getLocalizedName(context)), - ], - ), - ); - }).toList(), - onChanged: (EventStatus? value) { + DropdownMenu( + initialSelection: _item.status, + dropdownMenuEntries: EventStatus.values + .map((value) => DropdownMenuEntry( + value: value, + leadingIcon: PhosphorIcon( + value.icon(PhosphorIconsStyle.light), + color: value.getColor()), + label: value.getLocalizedName(context), + )) + .toList(), + onSelected: (EventStatus? value) { _item = _item.copyWith(status: value ?? _item.status); }, - decoration: InputDecoration( - labelText: AppLocalizations.of(context).status, - icon: const PhosphorIcon(PhosphorIconsLight.info), - border: const OutlineInputBorder(), - ), + label: Text(AppLocalizations.of(context).status), + leadingIcon: + const PhosphorIcon(PhosphorIconsLight.info), + expandedInsets: const EdgeInsets.all(4), ), const SizedBox(height: 16), TextFormField( diff --git a/app/lib/pages/notes/page.dart b/app/lib/pages/notes/page.dart index bf3c760437e..59600d2e2e3 100644 --- a/app/lib/pages/notes/page.dart +++ b/app/lib/pages/notes/page.dart @@ -148,11 +148,13 @@ class NotesBodyView extends StatefulWidget { class _NotesBodyViewState extends State { late final FlowCubit _flowCubit; late final SourcedPagingController _controller; + late final Future _parent; late NoteFilter _filter; @override void initState() { _flowCubit = context.read(); + _parent = _fetchParent(); _controller = SourcedPagingController(_flowCubit); _controller.addFetchListener((source, service, offset, limit) async { if (_filter.source != null && _filter.source != source) return null; @@ -177,17 +179,21 @@ class _NotesBodyViewState extends State { search: widget.search); if (notes == null) return null; if (source != widget.parent?.source) return notes; - final parent = await service.note?.getNote(widget.parent!.model); - if (parent == null) return notes; - return [ - parent, - ...notes, - ]; + return notes; }); _filter = widget.filter; super.initState(); } + Future _fetchParent() async { + if (widget.parent == null) return null; + final parent = await _flowCubit + .getService(widget.parent!.source) + .note + ?.getNote(widget.parent!.model); + return parent; + } + @override void dispose() { _controller.dispose(); @@ -214,6 +220,24 @@ class _NotesBodyViewState extends State { return Scaffold( body: Column( children: [ + FutureBuilder( + future: _parent, + builder: (context, snapshot) { + final data = snapshot.data; + if (data == null) return Container(); + return Column( + children: [ + NoteCard( + controller: _controller, + source: widget.parent!.source, + primary: true, + note: data, + ), + const SizedBox(height: 8), + const Divider(), + ], + ); + }), NoteFilterView( initialFilter: _filter, onChanged: (filter) { @@ -229,24 +253,11 @@ class _NotesBodyViewState extends State { pagingController: _controller, builderDelegate: buildMaterialPagedDelegate>( _controller, - (ctx, item, index) { - final primary = item.source == widget.parent?.source && - item.model.id == widget.parent?.model; - return Column( - children: [ - NoteCard( - controller: _controller, - source: item.source, - note: item.model, - primary: primary, - ), - if (primary) ...[ - const SizedBox(height: 8), - const Divider(), - ] - ], - ); - }, + (ctx, item, index) => NoteCard( + controller: _controller, + source: item.source, + note: item.model, + ), ), ), ), diff --git a/app/lib/widgets/source_dropdown.dart b/app/lib/widgets/source_dropdown.dart index 75141617990..1dac79c83c8 100644 --- a/app/lib/widgets/source_dropdown.dart +++ b/app/lib/widgets/source_dropdown.dart @@ -37,23 +37,16 @@ class SourceDropdown extends StatelessWidget { return Column( children: [ const SizedBox(height: 16), - DropdownButtonFormField( - value: value, - items: services.entries.map>((value) { + DropdownMenu( + initialSelection: value, + dropdownMenuEntries: services.entries.map((value) { final remote = cubit.sourcesService.getRemote(value.key); - return DropdownMenuItem( + return DropdownMenuEntry( value: value.key, - child: Text( - remote?.displayName ?? AppLocalizations.of(context).local), + label: remote?.displayName ?? AppLocalizations.of(context).local, ); }).toList(), - selectedItemBuilder: (context) { - return [ - ...remotes.map((value) => - Text(value?.uri.host ?? AppLocalizations.of(context).local)) - ]; - }, - onChanged: (value) { + onSelected: (value) { final service = services[value]; onChanged( service == null @@ -64,11 +57,9 @@ class SourceDropdown extends StatelessWidget { ), ); }, - decoration: InputDecoration( - labelText: AppLocalizations.of(context).source, - icon: const PhosphorIcon(PhosphorIconsLight.cloud), - border: const OutlineInputBorder(), - ), + label: Text(AppLocalizations.of(context).source), + leadingIcon: const PhosphorIcon(PhosphorIconsLight.cloud), + expandedInsets: const EdgeInsets.all(4), ), ], ); diff --git a/app/pubspec.lock b/app/pubspec.lock index b87ac93498d..f12f12a5eb5 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -205,10 +205,10 @@ packages: dependency: transitive description: name: coverage - sha256: "0cec992efb6feaa6141779723bf0119d5f511ebd658aa5225d9bac53cb7e609e" + sha256: ac86d3abab0f165e4b8f561280ff4e066bceaac83c424dd19f1ae2c2fcd12ca9 url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.7.1" crypto: dependency: transitive description: @@ -246,10 +246,10 @@ packages: dependency: transitive description: name: dev_test - sha256: "71b1bd01bb2068794aa6aab9e45ecd5257b5f3b5ca52903b12fcb5d56fe862bb" + sha256: "8a4aaca13079c6ca8f96124eb9c4fb45b71629ea9f2af052e42634aacae573ae" url: "https://pub.dev" source: hosted - version: "0.16.2+3" + version: "0.16.3" dynamic_color: dependency: "direct main" description: @@ -383,10 +383,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_secure_storage: dependency: "direct main" description: @@ -489,10 +489,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: a206cc4621a644531a2e05e7774616ab4d9d85eab1f3b0e255f3102937fccab1 + sha256: e156bc1b2088eb5ece9351bccd48c3e1719a4858eacbd44e59162e98a68205d1 url: "https://pub.dev" source: hosted - version: "12.0.0" + version: "12.0.1" graphs: dependency: transitive description: @@ -714,10 +714,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_foundation: dependency: transitive description: @@ -795,10 +795,10 @@ packages: dependency: transitive description: name: process_run - sha256: ceacfac6d566a36c895d64edc7e429efb2d6b6303b5e28d5c13bc59fe6e8974e + sha256: "1cb96f835ec02ba1a35af4e6f99f937618023b6d2ffba88f0c0f1041af2e9f12" url: "https://pub.dev" source: hosted - version: "0.13.1" + version: "0.13.2" provider: dependency: transitive description: @@ -1096,10 +1096,10 @@ packages: dependency: transitive description: name: test - sha256: d983a57c33dde6d44b1fb8635f67c91f4b41d26cf227c147963affa97d63563d + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.8" + version: "1.24.9" test_api: dependency: "direct overridden" description: @@ -1112,10 +1112,10 @@ packages: dependency: transitive description: name: test_core - sha256: "2f866bf4b20c11327ac166ee6036bddafb7fe9e35505ff8324f788e66913f967" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.5.9" timing: dependency: transitive description: @@ -1136,66 +1136,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.0" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.2.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.0" vector_math: dependency: transitive description: @@ -1208,10 +1208,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "12.0.0" + version: "13.0.0" watcher: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index aa2072bc879..640138d36e7 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: intl: ^0.18.1 animations: ^2.0.8 http: ^1.1.0 - url_launcher: ^6.1.14 + url_launcher: ^6.2.1 path_provider: ^2.1.1 flow_api: path: ../api @@ -44,7 +44,7 @@ dependencies: url: https://github.com/CodeDoctorDE/phosphor-flutter ref: f370dd2c25d3ea51ffb8f1c7c183bc9f33889c82 path: ^1.8.3 - go_router: ^12.0.0 + go_router: ^12.0.1 flex_color_scheme: ^7.3.1 flutter_bloc: ^8.1.3 freezed_annotation: ^2.4.1 diff --git a/fastlane/metadata/android/en-US/changelogs/6.txt b/fastlane/metadata/android/en-US/changelogs/6.txt index 459cb2f68ba..60c6072bf10 100644 --- a/fastlane/metadata/android/en-US/changelogs/6.txt +++ b/fastlane/metadata/android/en-US/changelogs/6.txt @@ -1,6 +1,7 @@ * Add macos and rpm builds * Make calendar views switcher smaller * Disable PrivilegesRequired in windows setup ([#23](https://github.com/LinwoodDev/Flow/issues/23))* Fix portable build +* Upgrade dropdowns to material 3 * Fix showing wrong month in month view ([#28](https://github.com/LinwoodDev/Flow/issues/28)) * Fix portable build * Fix file name in start.sh From 61275a8e46b0b7bf805fc68fd3747c16687ed9db Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Mon, 30 Oct 2023 12:09:20 +0100 Subject: [PATCH 2/2] Add simple markdown toolbar --- app/lib/l10n/app_en.arb | 11 +++++- app/lib/pages/notes/card.dart | 51 ++++++++++++++++++++++++++-- app/lib/widgets/markdown_field.dart | 13 ++++--- app/lib/widgets/source_dropdown.dart | 1 - 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index ef8f72dd66d..12eedc2cdcb 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -270,5 +270,14 @@ }, "deleteLabelDescription": "Are you sure you want to delete the label {name}?", "label": "Label", - "startOfWeek": "Start of week" + "startOfWeek": "Start of week", + "headlineNumber": "Headline {number}", + "@headlineNumber": { + "placeholders": { + "number": { + "type": "int", + "example": "1" + } + } + } } \ No newline at end of file diff --git a/app/lib/pages/notes/card.dart b/app/lib/pages/notes/card.dart index 5434c2d88f0..6dc25de8445 100644 --- a/app/lib/pages/notes/card.dart +++ b/app/lib/pages/notes/card.dart @@ -1,3 +1,6 @@ +import 'dart:math'; + +import 'package:collection/collection.dart'; import 'package:flow/helpers/sourced_paging_controller.dart'; import 'package:flow/widgets/color.dart'; import 'package:flow/widgets/markdown_field.dart'; @@ -36,8 +39,10 @@ class NoteCard extends StatefulWidget { State createState() => _NoteCardState(); } +enum PastePositing { line, selection } + class _NoteCardState extends State { - late final TextEditingController _nameController; + late final TextEditingController _nameController, _descriptionController; late Note _newNote; late final FlowCubit _cubit; late final SourceService _sourceService; @@ -52,6 +57,8 @@ class _NoteCardState extends State { void initState() { super.initState(); _nameController = TextEditingController(text: widget.note.name); + _descriptionController = + TextEditingController(text: widget.note.description); _newNote = widget.note; _cubit = context.read(); _sourceService = _cubit.getService(widget.source); @@ -85,6 +92,26 @@ class _NoteCardState extends State { } } + void _addDescription(PastePositing position, String text) { + var description = _descriptionController.text; + final selection = _descriptionController.selection; + if (!selection.isValid) return; + final start = selection.baseOffset; + final lineStart = max(0, description.lastIndexOf("\n", start)); + description = switch (position) { + PastePositing.line => description.substring(0, lineStart) + + text + + description.substring(lineStart), + PastePositing.selection => description.substring(0, start) + + text + + description.substring(selection.extentOffset), + }; + _descriptionController.text = description; + _descriptionController.selection = TextSelection.collapsed( + offset: start + text.length, + ); + } + @override Widget build(BuildContext context) { return Card( @@ -302,12 +329,32 @@ class _NoteCardState extends State { ]), ), const SizedBox(height: 16), + if (widget.primary) + SizedBox( + height: 50, + child: ListView(scrollDirection: Axis.horizontal, children: [ + ...[ + PhosphorIconsLight.textHOne, + PhosphorIconsLight.textHTwo, + PhosphorIconsLight.textHThree, + PhosphorIconsLight.textHFour, + PhosphorIconsLight.textHFive, + PhosphorIconsLight.textHSix + ].mapIndexed((index, element) => IconButton( + icon: PhosphorIcon(element), + onPressed: () => _addDescription( + PastePositing.line, + "${"#" * (index + 1)} ", + ), + )) + ]), + ), MarkdownField( decoration: InputDecoration( labelText: AppLocalizations.of(context).description, border: const OutlineInputBorder(), ), - value: _newNote.description, + controller: _descriptionController, onChangeEnd: (value) { _newNote = _newNote.copyWith(description: value); _updateNote(); diff --git a/app/lib/widgets/markdown_field.dart b/app/lib/widgets/markdown_field.dart index 6fdffb4682f..67af71852cb 100644 --- a/app/lib/widgets/markdown_field.dart +++ b/app/lib/widgets/markdown_field.dart @@ -5,14 +5,16 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:markdown/markdown.dart' as md; class MarkdownField extends StatefulWidget { - final String value; + final String? value; final InputDecoration decoration; + final TextEditingController? controller; final ValueChanged? onChanged, onChangeEnd; final List actions; const MarkdownField( {super.key, - required this.value, + this.value, + this.controller, this.onChanged, this.onChangeEnd, this.decoration = const InputDecoration(), @@ -23,14 +25,15 @@ class MarkdownField extends StatefulWidget { } class _MarkdownFieldState extends State { - final TextEditingController _controller = TextEditingController(); + late final TextEditingController _controller; bool _editMode = false; final FocusNode _focusNode = FocusNode(); @override void initState() { super.initState(); - _controller.text = widget.value; + _controller = + widget.controller ?? TextEditingController(text: widget.value); _focusNode.addListener(() { if (!_focusNode.hasFocus) { _exitEditMode(); @@ -42,7 +45,7 @@ class _MarkdownFieldState extends State { void didUpdateWidget(MarkdownField oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.value != widget.value) { - _controller.text = widget.value; + _controller.text = widget.value ?? _controller.text; } } diff --git a/app/lib/widgets/source_dropdown.dart b/app/lib/widgets/source_dropdown.dart index 1dac79c83c8..3ecbd12d8b5 100644 --- a/app/lib/widgets/source_dropdown.dart +++ b/app/lib/widgets/source_dropdown.dart @@ -33,7 +33,6 @@ class SourceDropdown extends StatelessWidget { }) .whereNotNull() .toList()); - final remotes = services.keys.map((e) => cubit.sourcesService.getRemote(e)); return Column( children: [ const SizedBox(height: 16),