From 5647c9f59a82870f78bc2655f7647378dd233285 Mon Sep 17 00:00:00 2001 From: Cierra_Runis <2864283875@qq.com> Date: Fri, 4 Aug 2023 14:29:40 +0800 Subject: [PATCH] =?UTF-8?q?Features=F0=9F=8C=8D=201.=20=F0=9F=8C=8DTitle?= =?UTF-8?q?=20searching=20#13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/database/isar_service.dart | 18 ++-- lib/index.dart | 1 + lib/pages/mercurius/mercurius_home_page.dart | 20 ++--- lib/states/diary/diary_search_text.dart | 32 +++++-- lib/states/diary/diary_search_text.g.dart | 22 ++++- lib/states/mercurius/current_position.g.dart | 3 +- lib/states/mercurius/current_version.g.dart | 3 +- .../mercurius/github_latest_release.g.dart | 3 +- lib/states/mercurius/mercurius_path.g.dart | 3 +- lib/states/mercurius/qweather_now.g.dart | 3 +- lib/widgets/based/based_chip_widget.dart | 86 +++++++++++++++++++ lib/widgets/based/index.dart | 1 + .../diary/diary_search_bar_widget.dart | 65 +++++++++++--- lib/widgets/index.dart | 1 + pubspec.lock | 8 ++ pubspec.yaml | 1 + 16 files changed, 228 insertions(+), 42 deletions(-) create mode 100644 lib/widgets/based/based_chip_widget.dart create mode 100644 lib/widgets/based/index.dart diff --git a/lib/database/isar_service.dart b/lib/database/isar_service.dart index f9b25900..9403d2b0 100644 --- a/lib/database/isar_service.dart +++ b/lib/database/isar_service.dart @@ -16,17 +16,21 @@ class IsarService { /// 创建 `Stream` 监听所有含有 `contains` 字符串的日记 Stream> listenToDiariesContains( - String contains, { + DiarySearch diarySearch, { int delayed = 300, }) async* { final isar = await _db; await Future.delayed(Duration(milliseconds: delayed)); - yield* isar.diarys - .filter() - .editingEqualTo(false) - .contentJsonStringContains(contains) - .sortByCreateDateTimeDesc() - .watch(fireImmediately: true); + QueryBuilder diaries = + isar.diarys.filter().editingEqualTo(false); + + if (diarySearch.searchTitle) { + diaries = diaries.titleStringContains(diarySearch.text); + } else { + diaries = diaries.contentJsonStringContains(diarySearch.text); + } + + yield* diaries.sortByCreateDateTimeDesc().watch(fireImmediately: true); } /// 创建 `Stream` 监听所有 `editing` 为 `true` 的日记 diff --git a/lib/index.dart b/lib/index.dart index 2e27b355..eef027e7 100644 --- a/lib/index.dart +++ b/lib/index.dart @@ -54,6 +54,7 @@ export 'package:photo_view/photo_view.dart'; // 图片视图 export 'package:pull_to_refresh/pull_to_refresh.dart'; // 下拉刷新 export 'package:qweather_icons/qweather_icons.dart'; // QWeather 图标 export 'package:share_plus/share_plus.dart'; // 分享 +export 'package:star_menu/star_menu.dart'; // 浮动面板 export 'package:sticky_and_expandable_list/sticky_and_expandable_list.dart'; // 粘性头部与分组列表 export 'package:system_tray/system_tray.dart'; // 系统托盘 export 'package:syncfusion_flutter_charts/charts.dart'; // 图表 diff --git a/lib/pages/mercurius/mercurius_home_page.dart b/lib/pages/mercurius/mercurius_home_page.dart index 70f4658e..a3540990 100644 --- a/lib/pages/mercurius/mercurius_home_page.dart +++ b/lib/pages/mercurius/mercurius_home_page.dart @@ -24,16 +24,16 @@ class _MercuriusHomePageState extends ConsumerState { onPressed: _switchCurrentBarMode, icon: const Icon(Icons.search), ), - title: GestureDetector( - behavior: HitTestBehavior.translucent, - onPanStart: (_) => windowManager.startDragging(), - onDoubleTap: windowManager.center, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 500), - child: _searchBarMode - ? const DiarySearchBarWidget() - : const MercuriusAppBarTitleWidget(), - ), + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + child: _searchBarMode + ? const DiarySearchBarWidget() + : GestureDetector( + behavior: HitTestBehavior.opaque, + onPanStart: (_) => windowManager.startDragging(), + onDoubleTap: windowManager.center, + child: const MercuriusAppBarTitleWidget(), + ), ), centerTitle: true, actions: PlatformWindowsManager.getActions(), diff --git a/lib/states/diary/diary_search_text.dart b/lib/states/diary/diary_search_text.dart index 233f8e31..27cc6612 100644 --- a/lib/states/diary/diary_search_text.dart +++ b/lib/states/diary/diary_search_text.dart @@ -1,13 +1,35 @@ +import 'package:mercurius/index.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'diary_search_text.g.dart'; @riverpod class DiarySearchText extends _$DiarySearchText { @override - String build() => '\\n'; + DiarySearch build() => const DiarySearch(); - void change([String? newString]) { - newString ??= '\\n'; - state = newString == '' ? '\\n' : newString; - } + void change([DiarySearch? newDiarySearch]) => + state = newDiarySearch ?? const DiarySearch(); +} + +@JsonSerializable() +class DiarySearch { + const DiarySearch({ + this.text = '\\n', + this.searchTitle = false, + }); + final String text; + final bool searchTitle; + + DiarySearch copyWith({ + String? text, + bool? searchTitle, + }) => + DiarySearch( + text: text ?? this.text, + searchTitle: searchTitle ?? this.searchTitle, + ); + + factory DiarySearch.fromJson(Map json) => + _$DiarySearchFromJson(json); + Map toJson() => _$DiarySearchToJson(this); } diff --git a/lib/states/diary/diary_search_text.g.dart b/lib/states/diary/diary_search_text.g.dart index b46eed64..ca6db4fa 100644 --- a/lib/states/diary/diary_search_text.g.dart +++ b/lib/states/diary/diary_search_text.g.dart @@ -2,16 +2,29 @@ part of 'diary_search_text.dart'; +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DiarySearch _$DiarySearchFromJson(Map json) => DiarySearch( + text: json['text'] as String, + ); + +Map _$DiarySearchToJson(DiarySearch instance) => + { + 'text': instance.text, + }; + // ************************************************************************** // RiverpodGenerator // ************************************************************************** -String _$diarySearchTextHash() => r'aabff893c5663a62c2ebc4da1ab1bed17c9e947a'; +String _$diarySearchTextHash() => r'b2e5221700bda35c519b9ac82f0555e308c0fa04'; /// See also [DiarySearchText]. @ProviderFor(DiarySearchText) final diarySearchTextProvider = - AutoDisposeNotifierProvider.internal( + AutoDisposeNotifierProvider.internal( DiarySearchText.new, name: r'diarySearchTextProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -21,5 +34,6 @@ final diarySearchTextProvider = allTransitiveDependencies: null, ); -typedef _$DiarySearchText = AutoDisposeNotifier; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +typedef _$DiarySearchText = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/states/mercurius/current_position.g.dart b/lib/states/mercurius/current_position.g.dart index ead13877..32af42e8 100644 --- a/lib/states/mercurius/current_position.g.dart +++ b/lib/states/mercurius/current_position.g.dart @@ -43,4 +43,5 @@ final currentPositionProvider = ); typedef CurrentPositionRef = AutoDisposeFutureProviderRef; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/states/mercurius/current_version.g.dart b/lib/states/mercurius/current_version.g.dart index b0f18285..c86b39a6 100644 --- a/lib/states/mercurius/current_version.g.dart +++ b/lib/states/mercurius/current_version.g.dart @@ -21,4 +21,5 @@ final currentVersionProvider = AutoDisposeFutureProvider.internal( ); typedef CurrentVersionRef = AutoDisposeFutureProviderRef; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/states/mercurius/github_latest_release.g.dart b/lib/states/mercurius/github_latest_release.g.dart index 17157ea8..2c1e2875 100644 --- a/lib/states/mercurius/github_latest_release.g.dart +++ b/lib/states/mercurius/github_latest_release.g.dart @@ -76,4 +76,5 @@ final githubLatestReleaseProvider = typedef GithubLatestReleaseRef = AutoDisposeFutureProviderRef; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/states/mercurius/mercurius_path.g.dart b/lib/states/mercurius/mercurius_path.g.dart index e3722949..f9941d48 100644 --- a/lib/states/mercurius/mercurius_path.g.dart +++ b/lib/states/mercurius/mercurius_path.g.dart @@ -21,4 +21,5 @@ final mercuriusPathProvider = AutoDisposeFutureProvider.internal( ); typedef MercuriusPathRef = AutoDisposeFutureProviderRef; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/states/mercurius/qweather_now.g.dart b/lib/states/mercurius/qweather_now.g.dart index 8e4c552b..1bdd5003 100644 --- a/lib/states/mercurius/qweather_now.g.dart +++ b/lib/states/mercurius/qweather_now.g.dart @@ -93,4 +93,5 @@ final qWeatherNowProvider = AutoDisposeFutureProvider.internal( ); typedef QWeatherNowRef = AutoDisposeFutureProviderRef; -// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/widgets/based/based_chip_widget.dart b/lib/widgets/based/based_chip_widget.dart new file mode 100644 index 00000000..541e0133 --- /dev/null +++ b/lib/widgets/based/based_chip_widget.dart @@ -0,0 +1,86 @@ +import 'package:mercurius/index.dart'; + +class BaseChipWidget extends StatelessWidget { + const BaseChipWidget({ + super.key, + required this.label, + this.borderRadius = 8, + this.keepLeadingArea = true, + this.keepTracingArea = false, + this.labelLeadingSpacing = 4, + this.labelTracingSpacing = 4, + this.labelWidth, + this.labelColor, + this.fontWeight, + this.leadingIconData, + this.tracingIconData, + this.leadingIconColor, + this.tracingIconColor, + this.onTap, + }); + + final String label; + final double borderRadius; + final bool keepLeadingArea; + final bool keepTracingArea; + final double labelLeadingSpacing; + final double labelTracingSpacing; + final double? labelWidth; + final Color? labelColor; + final FontWeight? fontWeight; + final IconData? leadingIconData; + final Color? leadingIconColor; + final IconData? tracingIconData; + final Color? tracingIconColor; + final GestureTapCallback? onTap; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(borderRadius), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Wrap( + children: [ + if (keepLeadingArea || leadingIconData != null) + Icon( + leadingIconData, + color: leadingIconColor ?? colorScheme.onSurface, + size: 20, + ), + Padding( + padding: EdgeInsets.fromLTRB( + labelLeadingSpacing, + 0, + labelTracingSpacing, + 0, + ), + child: SizedBox( + width: labelWidth, + child: Text( + label, + style: TextStyle( + color: labelColor, + fontWeight: fontWeight, + ), + ), + ), + ), + if (keepTracingArea || tracingIconData != null) + Icon( + tracingIconData, + color: tracingIconColor ?? colorScheme.onSurface, + size: 20, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/based/index.dart b/lib/widgets/based/index.dart new file mode 100644 index 00000000..162db4c4 --- /dev/null +++ b/lib/widgets/based/index.dart @@ -0,0 +1 @@ +export 'based_chip_widget.dart'; diff --git a/lib/widgets/diary/diary_search_bar_widget.dart b/lib/widgets/diary/diary_search_bar_widget.dart index 8bc28535..cf69f65a 100644 --- a/lib/widgets/diary/diary_search_bar_widget.dart +++ b/lib/widgets/diary/diary_search_bar_widget.dart @@ -6,19 +6,62 @@ class DiarySearchBarWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final MercuriusL10N l10n = MercuriusL10N.of(context); + final ColorScheme colorScheme = Theme.of(context).colorScheme; - return SizedBox( - width: 160, - child: TextField( - autofocus: true, - textAlign: TextAlign.center, - onChanged: (value) => - ref.watch(diarySearchTextProvider.notifier).change(value), - decoration: InputDecoration( - hintText: l10n.searchDiaryContent, - border: InputBorder.none, + return Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + textAlign: TextAlign.center, + onChanged: (value) => + ref.watch(diarySearchTextProvider.notifier).change( + ref.watch(diarySearchTextProvider).copyWith(text: value), + ), + decoration: InputDecoration( + hintText: l10n.searchDiaryContent, + border: InputBorder.none, + ), + ), ), - ), + StarMenu( + params: StarMenuParameters.dropdown(context).copyWith( + boundaryBackground: BoundaryBackground( + blurSigmaX: 4, + blurSigmaY: 4, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + color: colorScheme.outlineVariant.withAlpha(32), + ), + ), + linearShapeParams: const LinearShapeParams( + space: 4, + angle: -90, + ), + centerOffset: const Offset(0, 40), + closeDurationMs: 200, + ), + onItemTapped: (index, controller) => controller.closeMenu!(), + lazyItems: () async { + final diarySearch = ref.watch(diarySearchTextProvider); + return [ + BaseChipWidget( + leadingIconData: diarySearch.searchTitle ? Icons.check : null, + label: '搜索标题', + onTap: () => ref.watch(diarySearchTextProvider.notifier).change( + diarySearch.copyWith( + searchTitle: !diarySearch.searchTitle, + ), + ), + ), + ]; + }, + child: IconButton( + onPressed: () {}, + icon: const Icon(Icons.expand_more_rounded), + ), + ), + ], ); } } diff --git a/lib/widgets/index.dart b/lib/widgets/index.dart index f607f640..3413a231 100644 --- a/lib/widgets/index.dart +++ b/lib/widgets/index.dart @@ -1,3 +1,4 @@ +export 'based/index.dart'; export 'diary/index.dart'; export 'dialog/index.dart'; export 'mercurius/index.dart'; diff --git a/pubspec.lock b/pubspec.lock index b29ae046..2bd27d41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1082,6 +1082,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + star_menu: + dependency: "direct main" + description: + name: star_menu + sha256: "8c5de0b5002dd09bee51bc4456c3424ba81e127a2b0a2a6e7b775fdacaed2a61" + url: "https://pub.dev" + source: hosted + version: "3.1.4" state_notifier: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fdb119ff..01927da7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: qweather_icons: ^0.0.11 # QWeather 图标 [https://pub.dev/packages/qweather_icons] riverpod_annotation: ^2.1.1 # 状态管理相关 [https://pub.dev/packages/riverpod_annotation] share_plus: ^7.0.2 # 分享 [https://pub.dev/packages/share_plus] + star_menu: ^3.1.4 # 浮动面板 sticky_and_expandable_list: ^1.1.2 # 粘性头部与分组列表 [https://pub.dev/packages/sticky_and_expandable_list] system_tray: ^2.0.3 # 系统托盘 [https://pub.dev/packages/system_tray] syncfusion_flutter_charts: ^22.1.39 # 图表 [https://pub.dev/packages/syncfusion_flutter_charts]